C#/WPF - Building an web cam app

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everybody uh welcome back i apologize for my long hiatus uh it was good to have a christmas break and just had a bunch of stuff i was dealing with and so uh i do apologize for being gone and absent and i actually took a break from coding for several weeks which was very unusual for me but felt good in the end to have a little bit of breathing room so back and ready to start coding again tonight i'm going to go back a little bit towards a bit of my roots of why i got into programming so usually when people are interested in programming they always ask you know what language should i learn which what should i do and my answer is always find something that that you're interested in or like and and build that right the language is less important than being interested in it and um wanting to actually build out what you want so that's initially what got it me interested in programming um and so tonight we're actually going to build a wpf app for christmas i was given a 3d printer which has been both a source of great fun and great frustration and part of that is there's there's lots of software out there that will do what i'm wanting to do tonight however i am a developer and the developer's default setting is write code so i am going to embrace the long-standing tradition of all developers and write in code so i have a webcam that has been sitting in a box for a very long time that isn't really doing anything and so i decided that uh initially this camera i had thought about turning into like a baby monitor or something and tonight we're going to modify that idea slightly and turn it into uh a camera so that i can keep an eye on my 3d printer that's that's literally the the end goal so let's let's look so what i've got and i don't think this is the exact model but it's the closest thing i could find it looks kind of like this it's sitting behind me we'll see it in a minute um but it's a little network camera that i can that i can use and and drive it around and point at it and if you go and navigate to its ip address i've got it on a static ip address of 192.168.085 right now it's got some cool stuff and you'll note hi we are now inceptioning ourselves between you know my screens and uh how my setup looks so but you'll note it's got this kind of little crappy web interface which is which is fine and if we if we refresh the page you'll note it goes back to this lovely view where you've got for browser with ie kernel if that gives you an idea of how old it is i've had this camera for many years and then also this mode two and then image mode which is which is really exciting um if you look at the the dev tools and check the network thing you'll note what it is doing is literally just spamming an endpoint to download a jpeg as fast as it possibly can and that's how it's doing video don't laugh when uh my son was in the icu this is literally how their cameras worked when they gave it to us so apparently this is this is normal so you get this nice pretty network traffic you will also note that as part of this url it's literally putting the username and password directly in the query string so i have i have set it back to a default username and password that i intend to change later but for now this will do and then it appears to either be counting or a random number i'm not sure which and i'm i'm guessing this is just for cash busting so that it always gets a new one but we're going to be a little bit more intelligent so let's let's let's get off of that that that seems like a bad idea okay so we'll go we'll go back here and more importantly this thing's this thing's got some cool options where i can drive it around and uh change some resolution and adjust brightness and you know go ir mode on it in the night time which is not going to do much right now and that kind of thing so we want this or something similar to this into a wpf app that's what we're that's tonight's end goal so to that end i put together a small wpf app so it is a file new project dot net five all the things right so we're going latest and greatest uh and then i brought in this project here called ffme for windows so this is a um a slightly different it's a media element drop-in so on my main window i've literally created this ffme media element right so it's it's me the wpf does have a built-in media element but in our case we want to use this one the short version is i tried this video stream on the the regular media element earlier and couldn't get it to work so something's off and rather than troubleshooting it i'm like you know what let's go with something ffmpeg-based ffmpeg is the de facto standard for all things video we'll use that so and i've got some experience with this project and i was like it's easy enough to plug in let's do it the one thing to know about it when you bring it in it's kind enough to give you this nice little read me with some directions so i've kind of gone through and done this already so the first thing to note is it doesn't distribute ffmpeg with the nuget package it expects you to go off and download the appropriate binaries yourself which is fine so if we follow the directions right so we go to this and then we come over to windows and then i like this second source down here because it takes me to a github page and i like github better and so in my case i came down here and went okay you can either grab the the ffmpeg with gpl stuff included or the lgpl stuff i went for the lgpl i don't think it's going to matter for this but i pulled this down went great uh it the documents do explicitly point out you want to gather one of the shared binaries that's important right for the for how it's doing the linking in to the native lib and then either 32 or 64 bit i'm on 64-bit if you're going to distribute this to lots of people you might consider doing something fancy where you toggle based on bitness in case you want to support 32-bit operating systems for the three people that will still have them uh and then finally you need to set up this static directory so it knows where to go so in my app.xaml i've literally just i've hard coded a path this app is designed for for me running on my computer so so it's going to work for me we'll see about making it friendly for other people but for now just me just me is good enough for now uh let's see and then this is all about plug it into your to your stuff in your xaml and i went through and did that and so in my my main window on my media element i'm literally just calling open and passing it through to that same cgi one and there's the url and so the end result or i should say the current result is compile hand waving hand waving hand waving hand waving it's on the wrong monitor but look at that high little delay little lag good enough for what i intend to do use it for this will be just fine so we've got that going that sounds great i may come back because right now i don't actually care about having a nice pretty stream um i care more about getting the the camera functional and getting this app working and making this app pretty so that's where we're at let's see where we want to go next since that's the the sum total of where i'm at so a couple things that i'm thinking i want to do with this one material design because i want this app to not make my eyes bleed when i look at it and i think the the the key features that i that i think i'm looking to get in is i would like to have the um the little navigationy buttons for driving it around i think that would be nice and it should be pretty easy to reverse engineer this thing i think it's as simple as just grab these urls and look at what they're doing so that seems easy enough um i'm not sure if i need a muck with the resolution or not i i'm fairly confident because i've opened up multiple instances of this browser page and it appears toggling this toggles it on the camera not for the browser instance which is just awesome so i'm thinking we could probably get away with that and then you've got these presets where you can just ding ding have some settings and then zero back in on it and just say reset back to that which is kind of cool so i think let's let's see about putting in some sort of these navigation buttons maybe we should do let's do material design first that way we can make the buttons pretty let's make let's make the buttons pretty okay so let's go get us a nuget package browse material design i search for it every so often we'll grab this one i actually we're gonna we're gonna go crazy we're gonna grab latest yeah latest preview package go big also over the holidays i discovered there is a new flavor of mountain dew out at least in the u.s so called major melon i kind of like it it's pretty cool so how to make me happy discover new mountain dew flavors uh okay so we got material design in there and let's go through oh i didn't i'd been thinking i should have used my new project template um but we've got a couple things we can do here so let's go and do i think i've just got it sitting right here so we're just gonna grab some grab some stuff right out of the demo app real quick so we'll grab a bundled theme yeah so we'll grab the bundled theme in uh let's see app.xaml and let's see resource dictionary resource dictionary merge dictionaries poof drop that guy in auto code in the name space include uh now for the big question what should the color theme be that's a good question that's a that is a great question i i do not know so the base theme i'm just going to go with inherit so that it can toggle back and forth uh let's see hmm thinking of indigo accent trying to decide what goes what goes well with indigo maybe we go with a light blue primary and indigo accent yeah we're gonna go with it uh let's see so this guy will become a light blue indigo something like that cool and then for those people unfamiliar with it this color adjustment just makes sure that everything's gonna um it basically says tweak the colors so that i get good readability between my foreground and background so it sacrifices exactness of the colors in exchange for better readability of your app so that that is the trade-off with this color adjustment setting and then base theme of inherit says yeah if you're on windows 10 figure it out use the the windows 10 theme or if we can't figure it out fall back to light thing so good enough again this is just set to work for me which is great okay uh let's see so there's that there's that oh we need the default resource dictionary as well so that's this guy and yeah okay so now we come back over here and then the main window has a there's a new style that got added in the 4-0 stuff which is part of why i want to use it which means i get this for free so i drop that in and now now my window should look party why you window does not match target type of element window well i think you just need to compile because mr designer i'm pretty sure you don't know what you're talking about okay so that at least proves our background is now going dark theme good enough okay so let's let's talk layout here um i'm thinking what we're gonna do is we're gonna shift this media element to the side maybe and just have the controls on the left is that a good idea yeah let's start with that for now uh so we will do a grid with two columns now let's see with let's do 200-ish give or take a little bit uh take you grid column one kick you over column definition fill the remaining space so something akin to that that seems good okay so now we need to do something that's going to give us kind of an up down left right control trying to think i don't think there's anything really built in that lines up with this nicely so i think we're going to end up just in the button mode i'm kind of feeling like this icon button might be the the cleanest approach i think but a flat button might be just as good we might just go with a straight flat button yeah let's just do a flat button i think so we will just grab that guy and let's go build out some buttons so i think here what we're gonna do is let's start by inserting another nested grid um the app has the diagonals but i don't know if i care let's just do up down left right i think for the moment so um actually let's just do a dock panel then i guess if that's if that's the way we're going so we will do a button something like that and i need an icon i need me an icon icon something about maybe an arrow uh let's see i kind of like these ones here these kind of um bold outline ones so yeah we'll go with that for now uh so let's see we'll close the button and we will make its content that guy uh you probably want the include there you go yeah something like that so i think what we'll do is uh vertical alignment top maybe so i usually don't like the designer that much but i think that's that's kind of where we're gonna go okay so and then we gotta tell it where on the dock panel we want it to go otherwise it'll do last child fill so we will do doc this is to be bottom and then i assume there's bold arrow up or similar right where is the up arrow uh did let's see let's just do bold outline uh that one pretty please poof okay so that and then i need you to be docked top okay so that gets those two there uh i guess while we're in here let's just keep going uh let's see i tend to think in terms of how the like the margin goes so uh doing uh left top right bottom that tends to be the way my brain works so we're gonna go with that uh arrow just a guess it works so that goes there and come down here we'll do you on r-i-g-h-t uh let's see arrow right and then last child phil false okay so kind of got this weird layout and this is usually why i don't go with dock panels they always bite me in weird ways why are you down here oh because the order of the dock panel uh uh this is why we don't use dock panels i okay going back to a grid because grids are easier to get the layout i want so let's see column definitions uh let's see column definition i think we're just going to do with auto so here's here's what i'm thinking uh boom boom right i think i get away with i think i get away with the auto by leveraging the size the buttons but i have a something that back my mind is telling me that the the width and the height are going to be a little different so let's change that up something like that and then that means all of you guys need to change so let's be explicit so this is left so column zero grid row one all right and then we're just going to grab both of you to do and replace these guys so let's see up is gonna be column one row zero this guy is going to be column two uh row one and this guy same thing column one row two all right something like that see now i get the layout i want this is why i use grids this is why we use grids so let's see and then let's do horizontal alignment let's just center it up for now something like that and a little margin on the top to make it feel uh a little less cluttered yeah something like that okay so we got buttons they just don't do anything perfect that's exactly what we wanted buttons that don't do anything okay so let's go and grab because we're gonna do mvvm because that is the way the way i like to think um one second because i don't remember my i don't remember the package that i want for this so i'm going to go and grab it from another project that i have because i cannot remember the name of my own or of the nuget package i want microsoft toolkit mvvm that's the magic sauce we want so we'll use that and you do need to go preview packages because it is not released yet so for anybody who has used mvvm lite this is a i will say port of it it has been brought over it's for the what is it called windows community toolkit um and it's based on mvvm lite so if you're familiar with their names and apis it looks very similar but it is significantly more performant which is awesome and has been updated with all the latest bells and whistles and whatnot so okay so name spacing's optional for now let's see so main window view model observable object uh observable object come on there we go points for spelling points for spelling so we'll just slap that in there okay so we're gonna need a couple commands so i command uh how about move camera let's do move camera command i i go back and forth on whether i like suffixing my command properties with the word command or not but we'll go with it for now and then we'll have this guy taken to enum camera direction so we will do on specified and then left top right down something like that um it's always a good idea to sp to have a default value on your enum for your zeroth member um to be something like none unspecified or whatnot just so you have a way of detecting when it wasn't actually specified because all enums have a zeroth member whether you specify it or not so just be aware of that uh okay so let's see move camera command gets new relay command of camera direction come on do that thanks uh on move camera so this is just the the naming scheme i tend to use so for the um action methods for my commands i tend to prefix them with the word on and then name of command so let's see let's give this a better name direction okay so now we got to do a little reverse engineering um because ultimately what needs to happen and at some point we may want to change up the way we're getting these urls but here when i click button so for example up does this so if we nope nope nope nope copy uh link address sure that'll work thanks so there's the up command and so we've got this bit at the beginning which is pretty much just the address we gotta hit this cgi script we got a pass a user and a password and then we need a command one step apparently and then some what looks like some big cash busting variable and then an equals and a number okay that seems a little weird um i'm guessing command one is the key direction so let's let's test the theory so theory goes like this if i hit this endpoint directly right i assert that it should give me um a slight move upwards does not that's unfortunate so i'm going to need more let's go back over to here so i want to look at what all this does so when i let's clear this so when i click the up button it does two commands command 0 command 1. oh dear heavens uh that's mildly terrifying i'm hoping one isn't go and one is stopped because that would be absolutely hysterical so we might be looking at the ceiling here in just a moment um okay so let's copy link address we'll slap this guy in here just so we have a reference point but i believe and again i think the other stuff is just being tacked on for cash busting so i don't think i need anything after this because i think this stuff here is just random cache busting stuff so that browsers don't don't cache results for a given url so i think this one here so let's test this theory so if i take that one wow is there a way to collapse these things okay so i just want to confirm the url i'm about to set so command zero one step zero yeah that's apparently go and so that's what my ceiling looks like for anyone who is curious so that's exciting okay so what happens if we do one step make that a one really really [Laughter] so apparently one is go one is stop uh okay but apparently there's a one step parameter that will do one step just just hypothetically will it do two steps this is a great way to find a buffer overflow somewhere no there's there's apparently zero in one are the choices okay okay so let's let's get a couple things going here so so step one we need to do some uri building so we got to build up this so for now we're not going to leave it like this but public const [Music] camera address naming things is hard so let's do this uh and this will be 192.168.0.85 because that's the static ip that i set uh you know what we should do we should do this right let's do this right so let's put it so for the people who have seen any of like the unit test stuff that i've done um and for people who are interested uh there is a there's a link to it out on my youtube channel my pinned video is on a talk that i did on unit testing so if you want a lot more details on this go check that video out there's also going to be some blogs coming on uh my company's website that i'm writing on similar topics so here what i'm thinking we do is rather than clutter up the view model because the view model's purpose is to support the view right and it needs to delegate out from the uh command to go and make the camera do something but this level of detail about the uris and what all these things mean that's really specific to this camera so i'm thinking what we do i camera you know what how about just interface i camera you know what and we will we will do this um and i think we are going to probably violate the interface segregation principle for now but i think what we will do is do something like this uh so let's see uh what are we gonna call it so let's do let's just let's assume asynchronous um interaction because it's going to be like network calls and whatnot regardless and then hypothetically if i get a different camera i can replace just that that makes sense and more importantly it allows for easily testability and all all that jazz so that would be cool uh so let's see uh move camera i almost like uh pan right i kind of like pan as a the verb for where it's going so how about pan up right i think we'll just do this uh and then we will fix the directions so left up right down right so we'll we'll create that up this guy's gonna take in an eye camera or he should actually probably instantiate the camera for now at some point we'll have to come up with some way for him to get a camera but for now we're just gonna let him uh actually we will make him private uh camera camera boom so we're just going to do this and our implementation class will be called i don't know shield camera because shield is the name that's printed on the side of it so good enough for me good enough for me so you off to your own file uh you get implemented you off to your own file okay perfect so what we will do here is we will then do a switch on this uh direction then we'll do case and we will just basically split out all of these so left is going to be camera because i i kind of feel like i wanted to just do one step each time i click the the button i don't know if i need much more maybe we'll tie in some um keyboard commands so i can drive it with my arrow keys that's probably a nice feature that sounds like a very nice feature but the key part here is uh i don't top is the wrong up um let's see pan up and boom boom let's see right this gets changed to right uh down right and down okay so there's all that there's that we're gonna grab these two little urls and we're gonna go off and play okay so in general uh what we're going to need for this is going to be an http client now http clients are a bit special and if you look online you'll find a whole lot of information there's you can run into problems um if you just create yourself a static http client because it won't update or won't get dns updates now in my case i'm hitting a a fixed static ip address on my local network i could care less about dns updates so for now in the interest of simplicity private static http client uh we're just gonna do that because if you don't care about dns updates this is the easiest way to go about doing it so in general don't do that there's wonderful documentation on microsoft's site about better things with either named clients or similar so that you can get the best of both worlds um uh the other thing is uh this guy is i disposable do not dispose him after every call you will starve yourself out of sockets so it'll work for the first 65 000 give or take calls and then it'll sporadically start failing on you so uh that would be bad okay so now let's let's get a few things going so uh private const camera address i keep forgetting my type names because reasons so 192.1680.85 and then let's make ourselves just a little helper method real quick uh uh do you plan on displaying the camera feed on the wpf window yes so let me fire this up real quick and i will show you metal slug right now and i should probably just commit i should push this up to a github that's what i should do i should put this up on github and then i can commit as we go so right now hi we are actually live on a camera feed it's a little jerky i don't know how much that's coming through on this stream as far as you know actual working um and i i i have some hunches as to why it's a little jerky there but i am i am using a library called um ffme which is a ffmpeg based uh media element replacement so that guy is right here so actually real quick are we in a compiling state i think we're going to compile this date perfect we're in a compiling state add to source control let's let's push this thing up to github real quick and then uh let's see it doesn't need to be private repository app for me to watch my camera that sounds like a great description uh [Music] yeah that looks good push so here in a moment through the magical power of visual studio oh i just realized people probably couldn't see what i clicked on that bottom button down here there's a little add to source control button you click it and you pick get and that's how you end up in this situation so if we go here we go here so let's jump back over here and okay simply view so if anybody wants to go and play with the repo or watch what we're doing i will push changes at the end of the stream too uh i want to stream a raspberry pi camera to a wpf app so i'll definitely check out fme yeah and you might be able to get away with um the the standard media element that's right inside of wpf um hang on oh it created the repo but it didn't push it oh that not very nice uh open git changes two outgoing commits so push um the the disadvantage of the built-in wpf media element is it's limited on the the types of codecs and video stream types that it'll handle um i believe it only handles and i might get this backwards it only handles directx based stuff or it can't handle directx based stuff i i can't quite remember but ffmpeg is basically the the swiss army knife of all things video and so ffmpeg can handle pretty much anything that resembles a standard video stream and the docks on it are vast and occasionally really really complicated but so but this makes it pretty easy to use and for the most part this is a straight up drop in replacement for the wpf media element most of the apis are pretty similar if not identical or at least named close enough to where you can guess your way into it but you are correct at some point we should probably right now i'm literally just hard coding this guy right here to call open on it and we'll have to mvvm this up a little bit but for now just get it working first then make it pretty okay so we were uh we were gonna make ourselves a little url builder for this so static string uh how about build pan url that sounds like that sounds like a good thing to do um so let's see var builder gets new uri builder or url builder can't remember which it's called uri builder boom that's what i want that's what i want uh what do your constructors take uh hey show me your constructors come on not helpful okay navigate that's the one thing that always bothers me okay string uri scheme host port number hmm i think we just pass oh i don't have noble reference types turned on fine we'll go do that real quick because it's gonna suck if i do it later noble enable boom noble reference types on um i wonder how many things that just cause problems for i think we probably just go with this scheme and host name yeah let's do with that so scheme uh if i'm not mistaken there is pretty much no encryption on this yeah yeah they put the username and password right in the query string i can't imagine security kind of optional at that point uh let's see camera you don't have to worry about somebody sniffing your your traffic if you're just gonna literally put it right out there for everyone to see um okay so that gets me this much of the url and then i guess we could probably get away with uh i guess if we do port path so if we do port 80 all right and then path value in this case we know what it's going to be it's going to be this guy right okay and we will want to change this later as well but private con string username this is a this is a bad way to do security by the way horrible um so the amount of the amount of dumb in that one line is incredibly high that wasn't obvious uh so let's see we are going to uh i believe there isn't there uh like an add query string option oh is it just straight up query why did i think there was a like a cr worry hang on i could have sworn i could have sworn there was a query string builder uh blah blah constructing it using string builder that sounds horrible uri builder isn't that what we're using yeah oh yuck oh yuck uh fragment string empty okay so we're gonna have to we're gonna have to we're gonna have to get a little fancy so uh let's see i numerable string uh curry string all right so i guess we'll do that um so let's see yield return and we're going to do let's see it'll be login use equals to do username and then this is going to be password and it's just login pass with one s not two um and then in this case what we saw was command 0 was the go button so but i think we're going to do command 0 and then 1 step 1 so yield return uh because this is we almost surely need to pass in a different value for different directions um but we will get that later one step true so something akin to that and then builder query gets string join uh let's see here so we are going to uh um because the only real difference between the query string is this thing's just going to tack on the question mark for us and so we just need to put uh little ands between all of them so and then query string parts so [Music] what are you suggesting here make function static that sounds like a good idea and then how about return builder to stream so build build that guy up so we're gonna collapse a bunch of these things down for now real quick because we're just going to make up work and then i assume most of these are probably going to end up being pretty much the same so http client get a sync and then build pan url something like that await async we should probably do like some level of testing on response codes and that kind of thing but we just want to see it work real quick okay and then last thing we're going to do uh data context gets new main window view model and i think oh we didn't bind anything to our commands that is not going to work so uh that is left but i guess we'll just do them all over here uh binding oh you don't know your data type so let's let's get some intellisense going on here uh d data context gets d uh design instance type don't help me local main window view model boom stop being helpful figure that out uh oh this is x type does that make you happier good okay so now if we build now we should get useful intellisense on our bindings okay there you go so command binding and then really nothing despite the fact that i literally just told you what your rubble grubble grubble move camera command so were you not in there oh it is okay i just can't read and then we want to pass our enum value so this guy is going to be uh let's see local colon camera direction right camera direction uh left right that looks correct i think perfect so then we're gonna just grab you guys and kind of duplicate you down so you become up you become right and you become down okay so just to make sure things are working we will grab it here you are unhappy about something ah because camera is not declared that's an excellent point we will fix that in a moment something kind of like that okay so there's that we press the button we hit our breakpoint it passed the direction up so we should step into here this guy here is going to build us up the url let's come in here and see what he returned uh let's see that looks kind of like what i expect i wish there was a way to switch back quickly to see the camera move let's see oh too many break points i just wanna i i proof is in the pudding does it go up oh it's moving oh yeah oop apparently i q too many commands oh does it just keep going without stopping oh that might be bad uh okay so this is what happens when you reverse engineer and you don't do it correctly so go back down please so we might need to send both commands the one the one step may just be too much going up and down well so what i i don't know at what point you tuned in metal slug but what we saw a second ago was that the the web app that i'm cut and i use web app very loosely here it sends two commands command zero and command one and if you just do command zero with one step zero it just keeps going so i'm assuming what we want to do is actually change this to send both [Music] so how about bool start drop you off right and then this will become to do so start we'll return uh zero oh enable and disable yeah i assume that's what i assume that's what it is so that is that is a working theory um so zero is go one is stop uh oh can't be applied well the application's running i know i know i know hang on uh true false let's just try that so go up and then stop going up and then we'll implement down so up that did not do what i expected uh [Music] so that was too many steps and then it kept going so let's go back here real quick and check so i want to a bring you back down where i can see what i'm doing there's me from the back hi um clear that so what we want to simulate is that button okay so there's two commands that get fired off uh how do i make you bigger can i i can't do that and then i can pop you out if i do this button and then i drag you back over here okay so if we look at this guy when we click the button it sent that but i wish i could make that a little bigger but apparently it just does not want to expand out but command is zero at the beginning and then command is one so i'm wondering if i just fired them in too rapid of a succession so hypothetical let's try this await task delay time span from seconds 0.5 that's a lot longer than what um that other one used but if there's going to be a it's going to be timing related that should fix it and by fix i mean i should see it go up for half a second and stop nope just keeps going what are you doing why do you not stop so i guess the question then is what what urls are being built let's just make sure we've got this right so start should be zero oh because i got these backwards start should be zero stop should be one right so on true command should be zero and then on false command should be one so that's the problem which means i might not need this delay which would be great so let's try this one more time this time with success okay so up there and we can slowly go up and stare at the ceiling because that's exciting now if we implement the down button we might not blow up and crash that's just that's a convenience feature okay so that that now works so we need to send a then b yeah i like it i like it okay so that that that makes progress so i'm gonna leave well i'm gonna mark these here real quick that was up let's find down yeah it is it is a very interesting api uh this camera is let's see i think i bought it eight years ago so it's a little old i will so to its credit it was it was built not in recent recent memory we will say okay so let's see what down looks like if i click the down button i got two commands so let's copy link address i'm kind of hoping that there's some symmetry here copy link address boom okay because it would be really really nice if of course naturally naturally there is no no rhyme or reason to these commands though apparently i have landed on them in the appropriate order zero one two and three that's kind of funny let me guess left will be four i just i just have to know now and if i go back i don't need you where is the window there it is uh left is four and five oh my word let me guess right is now five and six oh of course not right is six and seven four five six seven okay so that's that's mildly convenient um and also really obnoxious uh let's see it start command so i think what we're going to do is just we'll pass in the start command right and then start command plus yeah that that appears to be the algorithm uh or actually hang on this oh that was part of our issue is i put this on the one step thing rather than on the command so i'm just i'm just dumb cut paste and this should just be fixed in all cases we're just gonna pass one step zero in fact we might even be able to test omitting this and i suspect it'll work um so this guy down here is going to pass true and zero yeah i i'm also i'm actually realizing there's no point in passing this bool if i just if i just request the command i'm just gonna make the caller pass the command yeah i'm not entirely sure what one step does because it seems like one step goes one step at a time but doesn't quit going just keeps going it's like rather than just a smooth transition it just goes and then no stop command okay again it's like okay that's a weird api but that works but this means i can now go here i can go let's see two three i'm really glad that they made this so simple for me and then let's see left becomes async uh four and five and right becomes here let's see if we can figure it out auto code do your thing not perfectly right but close enough oh i love the auto code button okay um just because the symmetry is going to bother me put it like that that way they read up down left right okay okay so that gets me to there that gets me to there let's see if it works so now in theory i can drive my camera around okay so a little high so let's go down down down down left so right works left works no up down what did i mess up okay so what did i do wrong so up should be zero and then one zero and then one okay so here's what we're doing real quick our return value uh how about debug come on go get it debug right line that way i don't have to keep attaching and setting breakpoints and whatnot okay okay so down output window let's see here down down so it's doing two and three so let's go over here and grab this window and clear it set you to the side for a second down what commands did you send did i miss something uh command two command three am i missing something obvious here command two command three and this guy let's see my guy sent what looks like an identical url give or take a little bit so that's the let's just grab them both let's just go compare these guys uh where's code just grab me a new code window real quick boom and bigger and be big so these are the commands i sent and copy link address these the commands the browser sent copy link address weird part was this was working so i've done something wrong so we can eliminate the port as the cause oh that's right the keystrokes on this are slightly different okay username password command 2 3 one step zero so unless these query params are set up here i don't believe they're doing anything those should just be cash busters so let's go back what is going on here so when i send this oh it is working it's just albeit slowly okay so it is going and it seems like it might be potentially i almost want to change it so that it doesn't send the stop command until i lift up or something but down works clear that why does this feel so non-responsive it's almost like it's sending it too fast i am wondering if there's a if my commands are going off too quickly and there needs to be a bit of a delay um let's go through and add in just a teeny await task delay from seconds let's just do um a small amount small amount of delay on each of these because i'm wondering if that's the issue is i'm sending start and stop so quickly that it stops before it physically gets going i generally don't like arbitrary delays in code but the exception is when you're dealing with physical stuff sometimes there's no way around it that seems to be a bit better but i'm wondering what i'm now wondering if what we do is we do this and drop this guy let's see auto code you are pretty much amazing auto code so that for those people who haven't seen that before that's intellicode and it is awesome okay so if we do one step that is not nearly as good as yeah we're gonna go back to the delays so undo undo undo okay so we will leave that with the slight delay and i think that's probably better it still bothers me that i'm getting slightly different behavior on my call versus what this thing is doing because i don't understand why it would have oh interesting this guy has the same responsive issue as well except for they they must be setting the ah so i think they must okay i bet you that's what it's doing is it sending one on mouse down and one on mouse up i bet you that's how it's doing it so on down up yeah so these are wired to different events ah ha got it we might have to wire something up like that too let's see what should we do well let's start with this working state uh let's uh heading pan commands uh failed to push the branch see the output window what one outgoing commit i'm pretty sure you pushed successfully pushed so yeah so that should have there should be there should be code out here uh let's see why do you know yeah oh whoopsie i checked in ffmpeg binaries well that was dumb uh we'll leave them there in case people are curious but uh yeah i shouldn't have done that oops okay uh that's what happens when you check stuff in like that okay so oh i probably should have left it like that for now uh that's fine we'll circle back to it um okay so let's see so that's actually an interesting ui effect to do right so if i if you wanted to go through and do this and effectively trigger something while the command was down and then stop it when the user lifts up how would we go about doing that so i'm thinking there are a couple ways to go about doing it so one i'd i think i need to change so this view model no longer reflects how this thing actually works [Music] so i think what i actually want to do is change up my interface uh start pan right i'm trying to decide what the api on this camera should look like i'm i'm wondering if maybe what there should be is yeah this is probably good enough no point in making it too complicated so we'll do that so we'll move the switch down inside of this and deal with that so first of all implement interface so what we are going to do is this guy here is going to be a uh so private static int get start pan command camera direction direction direction which uh all right i'm not oh i need another lambda lambda all the things and then this will be camera direction wait am i am i getting this backwards cannot implicitly convert and especially because that's what i want uh okay boom so up down left right so let's see uh up is zero down is two and then four and six four and six right and then other otherwise we'll just throw a new valid operation exception just have a google that sounds reasonable and then dupe this up for the stop commands and actually i think what we're going to do is get uh stop pan command get start pan command direction plus one boom and that will give me everything i need okay so that's enough to build the urls and so we will do this boom async let's see get start pan command direction async and you will be stopped why not okay so that gets the camera working for what we want where it's start and stop now we want to measure down and up trying to decide the easiest way to do this because i'm somewhat tempted to move away from this and instead do a instead do a a start stop or like a mouse down thing right so is um doesn't i think repeat button has a i thought there was an is pressed on one of these things because a repeat button would get me pretty close to what i want um because the repeat button so if we go here right um buttons uh let's see repeat button right so the repeat button here has the ability to just effectively as you hold it down it goes and fires we should really change this to show that feature off so as you hold it down it counts or something um i'm wondering if that's the best way to do it with a timeout but i that seems wrong because the alternative right is we do mouse down mouse up the only thing i don't like about that is that means that i can't use i have to do i have to handle keyboard events differently so if i tab all the way over to the button and press the space bar that's not going to count as holding the mouse button down and i don't know if i like that i don't i don't know if i like that as nearly as much down yeah because like uh i don't really want to handle the i wish there was an is pressed that's kind of what i want is a is pressed thingy what uh real quick let me sorry slight detour i want to look at the theme for this button and see because i think i believe there's a visual state which is how the ripple is working um because you'll note like here right as i hold it down the ripple grows but if i go quick and let up the ripple stops and i'm curious what that is bound to uh let's see ripple content what do you i don't actually remember how this thing works uh size change oh it is so it is watching mouse button events and going to particular states yuck yuck so the exact problem that i was going to try to avoid with mouse buttons i guess we don't have a lot of options i don't think we have a lot of options we go with mouse events i think yeah let's go with mouse events and maybe we do be a behavior for it that's probably the easiest so browse behaviors that's what i want pretty pretty please so people aren't familiar with this this is a great way if you find yourself getting ready to register up on events on a wpf control uh contemplate if a behavior is the right right option so here is the there's the link to the nougat so this guy here uh using behaviors and where is the don't they have a [Music] add this to your project well that's not very good documentation how is it that is not very good documentation what you need visual studio 2017 well so it's also old documentation got it okay so we will we will do this the fun way then so step one we're gonna create a um an is pressed behavior i think so is pressed behavior so in the event that i want to go through and do something more advanced later i've got a single place to do it so this will be a behavior and we will tie it to button base right so with behaviors you end up telling them exactly what type of control you intend to attach them to in this case this is the base class for all buttons in wpf uh and then there are a couple methods on here that you override so uh unattached and then override uh on detaching all right so this is where you now pretty much can start to do whatever the heck you want uh with the control so in this case we're gonna wrap up um our logic for when a button is pressed into this little object and then just apply it everywhere we want it so next thing we're going to do is uh dependency property uh the property dependency property boom and we will do a bool is pressed boom uh let's see here gotta include some stuff and the owner type is not set for me so we will slap that guy in there and default will be false not zero blah blah blah blah blah okay so we are going to store some stuff and the short version is when when this thing changes we are going to toggle this property so that everyone knows that it has been done so so after we are attached we will have an associated object which will be some instance of a button base and what we will do is we will watch for uh let's see mouse down i think yeah so we will watch for mouse down great apply sounds great and then similarly we will do mouse up so this will be this will be a somewhat naive implementation if we want to expand it later we can but so on down we will go is pressed gets true pressed gets false right so nothing overly fancy and then the only thing we're going to do a little differently bring that little guy up uh let's see to do is we want to then unregister these events as well when we when we're done with them oh oh on detaching so we will do that we'll unregister first so clean up our mess when we're done uh and register up when we start so something akin to that that means this guy here is gonna need some properties so private bool um up i don't like this name and that's the only reason i'm pausing uh because the word pressed implies some sort of ui and i really like to try and make my view models agnostic but it's also late and thinking of a good name is hard so something akin to that oh uh what did they call it it was called set property that's right like i said if you're familiar with mvvm light this syntax should be very familiar and we will just test with this so if this changes so the set property returns a bool indicating that the value is changed so that we will then do if value camera start pan camera direction up uh else camera stop pan camera direction up because i'm starting to wonder if i was to click those diagonal buttons if it was just a combination of starting commands for up and right followed by the appropriate stop commands kind of kind of wondering if that's the case so there is there is that we're going to just bind one of these guys to make sure that it works so we'll put you back to a button because you don't matter that much and we'll unregister the command and its parameter because that doesn't matter now and what we do need is let's see i enter activity activity behaviors believe that's correct um uh what is it interaction there we go that's what i'm looking for boom interaction behaviors and then we attach up our behavior so local is pressed behavior so doing this is ultimately what's going to cause that unattached method to get invoked oh hush you exist you exist i will compile you will be happy trust me it will be there um you are unhappy about all of that so for now this method does nothing okay see i told you you'd be happy okay and then so we want to do is is pressed binding um i already forgot what i called it is up pressed all right boom so let's real quick just break point this guy here to make sure he does in fact do kind of what we expect i realize the timing of this is going to be a little off but so that is not quite what we expect so let's come into here and verify that we are getting the mouse event because if we aren't getting that all bets are off okay so we didn't get the mouse event why oh is button hijacking the mouse event i wonder if button is hijacking the mouse event let's look because the fact that we registered up for it and didn't get it so everyone's favorite snoop if you're unfamiliar with it it is the best thing since sliced bread for wpf developers uh go and get it you will love it so in this case what we're going to do is we're going to use it to debug oh hang on we also got this binding failure what is that a property inspector data context property not found on object of type app huh that's interesting i am starting to i think that may have been triggered by when i attach snoop because i don't recall seeing that before okay so events what we are interested in looking at is mouse events so we're gonna turn off some stuff here so i went to the events tab on snoop after attaching it we're gonna ignore keyboard events mouse we want mouse down mouse up we don't i guess we should preview mouse down to see if it's getting hijacked all that jazz okay next what we do is we clear and then we come over here and we click the button once okay so this guy here shows us that the preview mouse down occurred so in wpf there are both what are called tunneling and bubbling events so tunneling events start from the top of your visual tree and work their way down bubbling events start from the bottom and work their way up so in this case you can see the preview events are usually tunneling so it started the main window main window got a chance to handle it said no no no no no no no all the way down to the thing that you clicked on inside of the button because you'll know here's the button and then grid border ripple grid all those things are inside the buttons template and then from there once the preview event got all the way down the bubbling event started working its way up so the grid from inside the template said no ripple said no border said no grid said no button said yes which meant we didn't get a chance to handle it because he hijacked us so uh i wonder if we can get up up uh i kind of uh while i've got this i kind of want to know do i get the am i allowed to get the up event mouse up handled by button okay okay we'll do this the hard way we will do this the hard way uh so let's see associated object dot uh what is the what is the register for event uh at event route there's a register for event and i'm not recalling what it's called uh because you can register to receive handled events as well oh add handler right add handler yeah okay so we want to add a handler we need the uh mouse up routed event uh how about button base and you will note that this guy has a two overloads come on and this is the bool that we're interested in this handled events two so we are going to give it our let's see this is down so we will feed it this delegate and then we want to pass a true so gimme is the short version uh yeah go ahead and simplify that and what is your problem cannot convert from method group to delegate uh so we need to turn this it needs to know the delegate type so routed event handler i believe is the magical incantation and that should make you happy um we'll handle various object center oh uh this is are you guys routed derived from rounded event args you are do i care i'm not using them so we'll just it's not happy that this doesn't uh rather than args so we will make you normally when you register up for it it'll handle all of that for you but okay so that'll get us down do the same thing with mouse up up event and then we're gonna grab this guy feed it in give me the handled events as well and then that also means that now what we need to do is we need to unregister that guy uh remove handler oh sorry there's a there's a bug here that i'm introducing that we need to address but we will deal with that in just a moment so the the issue here is that this is not going to work because the remove is different from the ad so we are passing in a different delegate so it's going to remove nothing uh which is perfectly fine for the moment we will just leak that handle while we verify that our add handler portion works so that's what we want to care about first so step one down hey hey i asked nicely why why why you not give me events yeah so you'll know no binding errors until i attach snoop so i'm gonna go with the act of attaching snoop and snoop looking at all the properties triggers the binding error so let's look at this events clear uh some mouse down should have gone right like we should have been able to see this uh why can't did i not get the event i should have been oh is it because i'm not part of the visual tree i wasn't considered because you'll know i'm not in this list of things to handle it oh yucky yucky yucky this is a metric pain um you could grab the preview for the down but that doesn't get us the up because it captures the mouse oh the button captures the mouse maybe i'm working too hard at this uh uh is mouse capture changed well let's just let's just try that approach then um because that will probably give me the same results that i'm looking for so if i do associate object uh got mouse capture right so we'll do that one thanks fine go away uh associate object lost mouse capture so we'll just assume that while it's capturing the mouse it's got access to it so uh is pressed gets true i'm a little surprised capture their any caf is mouse captured this is a dependency property so i should be able to just bypass this whole thing that was weird did i pop my window out i did not what i want a visual studio not what i wanted stay so i should be able to actually do is mouse captured and just slap this binding right on it and then don't even need the behavior me let's give it a try uh i probably left something in a non-compiling state is mouse captures read-only and can't be set from markup seriously that dumb that dumb so we'll go back to this approach then um hang on are the docs on that just wrong then because it claims that this is a dependency property there is a dependency oh that's an event there is a dependency property is it just because there's no setter that it can't interesting well we're gonna go with this because this should get us where we need to be get it working then make it pretty get it working then make it pretty must remember the order of operations so uh break point break point so down seriously associated object did not trigger on got mouse capture okay back into the snoop who is hijacking this so kinda wanna look and see let's see button is mouse captured so the thing changes so let's clear this uh let's see dude let's see we want to grab got lost mouse capture show me okay so you'll note as you click on items here it does select them over here and anything selected over here gets outlined here which is making me question did that get invoked it did associated object i got a button that's kind of what i expected i registered two events so why i am very very confused it created it it registered it for am i did i do this on the wrong button oh i've been pressing the wrong button this whole time our behavior is on left so that ain't gonna work let's try this again oh look detaching fired okay so all of that effort and all of that fighting and complaining and i'm clicking the wrong button awesome awesome let's let's just try this again and this might mean i go back to my register for the mouse down and up events but caption may be better i don't know so oh look got mouse capture okay so that that triggered that triggered okay so those those worked those got the value in this did not fire and i have a sinking suspicion it's that that is doing it to me so if i do that that triggers there that triggers there and nothing triggered on the binding so something did not bind appropriately here uh let's see oh i bet you i know what it is this thing is defaulted to binding mode one way i bet it needs to be a two-way binding mode two-way probably didn't need to relaunch but we're gonna do it anyway there's that there we go there's that and it actually worked so let's clear some break points um and see it actually work in action and let's see so that goes there that goes there let's clear some break points clear some break points clear some break points if we come back here we're pointed at the ceiling because break points are slow uh if we come back here we're gonna turn you down right and then we'll come over here and we'll go that worked pretty good i work pretty good i'll take it i'll take it okay so let's okay so i think i actually like working with the mouse capture over the mouse down and up events i think that's a little cleaner um so we are going to adjust this so it actually cleans up boom boom boom so that goes like that that goes like that uh we don't need you anymore that goes away that goes away that goes away in our xaml we are going to change these so this will be is left pressed oh do we want to change these things so we don't have to specify mode on them i think so so the reason the thing is binding one way is because of how we did this guy here so if you do framework property metadata this guy has an overload to his constructor that is framework options and one of the options is binds two-way by default so various properties in wpf um will have this uh value set on them specifically things that are meant for ui interaction so like the text property on a text box for example where it's expecting that when you bind to it you expect that communication to flow both directions whereas everything else binds one way by default meaning it'll just take the value from your view model up to the control so if you want two-way by default binding you change it from property metadata to framework property metadata and pass the appropriate enum value there this is also a flags enum so you can go through and do some fancy bitwise operations if it's going to do like met if this property causes things to be arranged or if you want it to be inherited down through the hierarchy that kind of thing um so kind of some fun stuff there but that's ultimately what is was breaking us and now because it will bind two way by default we can change these to drop the mode so let's finish these out and then i think i think this will be rather nice uh let's see i was doing it below you'll note these are attached properties this behaviors collection so i can pretty much put it anywhere inside of this button and it'll work it is different from the content that's being set here so just just note that uh let's see we are doing right right and then uh down boom and now i just need a couple things so one we're going to drop this command we don't need this constant here anymore that goes away that goes away that gets cleaned up and we're going to change these guys a little bit boom so let's do well let's change this one because i like things in order left left left and be very careful that we get all of these otherwise we're gonna have some fun bugs uh camera direction left camera direction left so that does all of those left up let's see i don't even need the constructor anymore paste so we already did left up we will do right right auto code figure it out auto code figure it out ooh apply for suggestions nope you didn't figure out the pattern sorry i know you think you know the refactor that i'm doing but you just you just don't some sometimes it is really awesome and great when it figures out the pattern that you're doing for your refactor and it just does it for you and other times it's not smart enough yet so very useful tool highly recommend turning on intellicode if you don't already um i do also highly recommend that you check its work because though it's good uh a hundred percent it is not so okay so that gives me all of those cleaning up my using statements and now we should have the ability to click and hold and let it go so let's see so we are pointed at the ceiling so let's go down okay right so even though the camera shows jerky movement um physically the camera is moving smooth so i think that jerky movement is likely just delay in the video player and i think if i i suspect this has to do with ffme it does a little bit of buffering on network streams and i think that's probably what's doing it whereas i believe the web page just dumps everything raw so i may take a look at digging through its internals at some point and see if i can strip those out but for the most part this is good enough for me to get this thing pointed where i want and then and it and it just works it just works that's pretty cool that pretty cool okay so let's go ahead we can uh let's just push changes so let's see added uh click and hold to pan uh commit all okay so trying to think of what else i might want for this so what would be what would be the most helpful so a few things i'm considering um the ir control might be nice to have to have a toggle for that and then also settings to control the camera stuff but the settings might be a little not needed for me right now because it's for me and one camera whereas the the ir is something i might want like now so let's go with that so let's go and look uh we need a toggly thingy so let's go find us a good toggle that looks pretty uh i'm kind of thinking one of these maybe uh yeah i might do one i i kind of like the accent one so give me one of those please and let's go back to our main window uh close all but this and so here we've got our grid i'm thinking i'm just going to rock the grid in a stack uh panel and to do that way i can put something below it i think right here is where we're gonna nope i can't type but that's okay that's okay copy try this again paste there we go so i want me a toggle button and let's see here so we'll leave the is checked for the moment and that style looks great so i should have a little toggle and maybe what we'll do is we'll put this guy in himself a stack panel right and make this guy's orientation horizontal just so that we can do a text block with some text that says i don't know ir boom all right so what we're looking to do is this button and the unfortunate part is for me to actually fully test the ir settings i would have to uh go and turn my lights off which would kind of make my video stream seem boring so we're just going to trust that this button does what it says it does and let's let's look at its network calls so i are on i are off okay so given that i'm i'm going to assume this guy is so camera control cgi gets toggled and there's a param and a value that gets talked on to the url so on so param14 value one param14 value zero so pretty simple uh let's see here and let's go and on our camera interface let's do something like task set ir mode is on all right we'll keep it simple we will keep it simple so implement interface we don't really need this stuff anymore so they'll clean that up and we don't need these commands anymore either and we do need this so this guy will become an async and this guy here uh so let's do another let's do another build bit here and i think what we're going to do because this is camera control cgi so copy copy link address let's go in here boom right so we're going to grab this so this camera control cgi bit is different here so we're going to change that that's fine um query string so login use is still the same login pass is still the same so that much we've handled this much of it uh let's see param and we are going to need so build uh camera control url sure uh and so we need an integer for the parameter and until we run into a reason otherwise i think what we're just going to do is param and value so [Music] parameter this guy down here becomes value like that yeah something like that so 14 and then i need to go zero and one so uh i think we can just actually come in here grab this guy boo and paste it in here and unindent unindent undent uh let's see and build camera control url and let's see so it's going to be 14 as the parameter is on pass a one else passes zero uh let's see build oh i nested too many things we just want that okay let's see let's see it work oh oh that ain't gonna work because we got nothing calling it uh let's see here so i wonder if there is a way to query that value i'm mildly tempted to so if i put this on right and then i refresh the page does it know that it's on it does so there is a way to query that get camera prans well that sounds remarkably like what i want uh so you hear don't you show me response well that's an exciting way to do this is clearly pre-json uh let's see hue saturation mode speed ir cut so i'm guessing ir cut goes to so let's let's copy this url copy link address boom we'll just go into this guy and paste that for the moment um and i'm guessing if i where's my doo doo not that this one i'm guessing that if i turn that value off refresh so clear if i refresh the page and go in and if i look at this yeah so ir cut is the one that does it okay okay so if we want to get the initial state we're gonna have to we're gonna have to query it out but for now what we're gonna do is we're just gonna blind set it so uh private bool is ir enabled and we will let it default to false public pool is ir enabled get will be simple just return is ir enabled the setter we will need to be a little more smarter so if set property ref is ir enabled value boom so if this value changes we will do camera set ir mode value so something that's worth noting here is these methods are async and i'm calling them inside of a property so these are effectively going to be fire and forget stuff so there is a teeny weensy itty bitty little bit of a problem for timing if i just start rapidly clicking on these things because these will all fire and the order that those http requests hit the camera is going to be kind of arbitrary so that's kind of that's the way that that flies because this starts the async call and then carries on its merry way and completes the property and releases the ui thread to handle the next click which is great it's fast it's responsive uh it's then sucky because well timing because async's hard okay it's not that hard so ir i are off i don't know how to test it oh i know how to test it okay so we think it's off right and what we can do is we can come over here and we can refresh this page and we should see it in the off position which it is i flick it on i refresh the page it should be in the on position it is not that is incorrect um let's confirm that we're getting all the way in we are oh we made the property and we probably didn't bind it uh yeah that'll do it boom okay so now if we go back here we believe it's on great it's passing a true excellent we're just going to assume that worked and then we are going to the fact that i saw that camera flash kind of makes me suspect that it is in the on position we come here it's now on we turn it off we refresh it's now in the off position sweet okay so i'm going to clean up that ui just a little bit because this kind of looks ugly to me uh so for starters you should be indented you need a little bit of margin around you give you a little bit of space how does that look as soon as i figure out where my window went that's a little better um kind of want to do some fancier text on this because uh uh style target type text block bum and base it on the built-in style uh static resource uh text block so this is how you effectively extend a style i know you're unhappy i get it go away uh so setter property i are disabled so we're gonna uh oh this becomes value the property is text what is your oh text block style goes there looks more like that and then we do a trigger to toggle it uh let's see so we'll just do a data trigger and the binding for this guy will be pointed at this same binding here right so and then when this guy goes to true we're just gonna toggle this text uh ir enabled so the only thing we don't have at the moment is the querying of the current state but that's okay that is okay by me so enabled disabled enabled disabled good enough i think the only other thing i want is a little bit of spacing um between those guys so it shows up a little nicer that looks a little better i like that i almost want to do a little eyebally thing with an icon but i also realized that it is bedtime for me and so i should probably go plant this camera down next to my printer and play with it and make sure it's gonna work for me so that i can watch it and it will be awesome uh let's commit this these changes uh so if anybody is interested uh adding ir toggle so and push those things out and thinking and thinking and successfully pushed so if people are interested that should all be out on this now so if we look here uh yeah addie and i are toggle so if people are interested in looking at it again i i'm building this largely for me it's tied to a camera that i have i don't expect this to be a necessarily a generic solution but somebody's interested um or at least just interested in how i went through and handled this i suppose at some point in time this media element i need to break this apart and expose out the the camera url you know what let's do that real quick public string video url uh camera get video url so i think we're gonna just add that real quick uh create method sure uh and actually let's do uri just make this a little because that's ultimately what needs to be there generate method sure and then we'll come into here we'll whack the string one and then let's go to shield camera boom implement interface for me and then let's come up here with this i think um well i guess i should keep some public methods together uh i think this guy is not using anything fancy if i'm not mistaken yeah just admin login and pass so i think what we are going to do is at some point i should really refactor this because we've got a little bit of duplication on each of these uh all right let me rephrase a lot of duplication uh build camera video url so we should definitely whack all of this stuff so that we get rid of this duplication mess i'm going to get rid of this debug right line because i don't think this one matters near as much and then why do i keep doing alt tab when i mean control tab video stream cgi so we'll come down here boom cycles like that that goes like that that goes like that this could be a straight-up return uh let's see boom boom this comes down here and this will be new uri kind of like that i think oh we can use a new expression thank you auto code button you are very helpful actually i should probably get myself in the habit should probably do it like this so for people who haven't seen it this is a new c sharp nine feature it o o auto code button yeah that's what i wanted see you can learn uh so a little bit cleaner a little bit less typing i like it better i it's i i have a sinking suspicion this is going to be the new var debate so that'll be fun um okay so that goes there that goes there main window so this guy here is going to change out and so rather than being this uh let's just capture the view model i think private main window view model view model so this becomes view model uh what is it what did i call it video uri boom so that should now work as well and now all of the camera stuff it can live in one one centralized spot and we have we have a camera and it moves that's pretty cool that's pretty cool i like it i like it i like it i like it okay uh let's go and commit that so [Music] moving camera url to shield camera boom commit all push okay so that is all out there and yeah whatever we won't push that change uh and ready for anybody who wants it so thank you everybody for coming out um i know there are some questions sitting for me on the material design getter chat room i i have seen them i briefly looked at them um my plan is to get that get to them in the next day or so um and hopefully get people answers i apologize that i haven't been quite as active i've been splitting my time amongst several projects and it has been tiring so with that i'm going to say goodnight happy coding we'll see everyone next week
Info
Channel: Kevin Bost
Views: 410
Rating: undefined out of 5
Keywords: programming, material desgin, C#, WPF, XAML, FFMPEG
Id: VkebsfMOeo4
Channel Id: undefined
Length: 132min 47sec (7967 seconds)
Published: Thu Jan 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.