Integrating some Rust in VLC media player
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 15 | |
Author | ||
License | CC Attribution 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor. | |
Identifiers | 10.5446/52241 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
Rust Belt Rust 20161 / 15
4
5
6
7
9
13
00:00
IntegerLink (knot theory)Beta functionPointer (computer programming)Dirac equationData bufferRead-only memoryHypermediaApplication service providerBuffer overflowFile formatParsingRun time (program lifecycle phase)ParsingCombinatory logicMacro (computer science)Function (mathematics)Error messageoutputType theoryAlpha (investment)Numerical digitString (computer science)Regulärer Ausdruck <Textverarbeitung>Data managementRevision controlExtension (kinesiology)Functional (mathematics)Multiplication signProjective planeVideo gameThermal expansionMessage passingParsingCombinational logicoutputImplementationBuffer solutionVulnerability (computing)Beta functionFunction (mathematics)Line (geometry)Macro (computer science)Point (geometry)File formatLatent heatAlpha (investment)Digital electronicsCartesian coordinate systemComputing platformFile Transfer ProtocolProduct (business)Point cloudInverter (logic gate)Instance (computer science)Virtual machinePlastikkarteService (economics)Functional (mathematics)Type theoryBitMereologyFinite-state machineData managementRow (database)Computer fileRight angleCrash (computing)String (computer science)Slide ruleResultantProcess (computing)Error messagePointer (computer programming)Formal languageGoodness of fitReplication (computing)CodeRegulärer Ausdruck <Textverarbeitung>HypermediaCellular automaton2 (number)Vector spaceDifferent (Kate Ryan album)Pattern languageElectronic mailing listSemiconductor memoryProgram slicingParsingStability theoryFuzzy logic1 (number)WritingRevision controlPattern matchingTupleIntegerLecture/Conference
08:32
Revision controlExtension (kinesiology)ParsingFunction (mathematics)String (computer science)Combinatory logicoutputData structureIndependence (probability theory)UsabilityPairwise comparisonData managementError messageGamma functionArchitectureCodeAerodynamicsModule (mathematics)System callEmailLink (knot theory)Image registrationParsingType theoryOnline chatComputer iconWrapper (data mining)TDMAOvalOpen setInterface (computing)PlanningParsingAttribute grammarInheritance (object-oriented programming)ResultantFlow separationBitKeyboard shortcutData structureSoftwareFunctional (mathematics)Module (mathematics)Streaming mediaoutputBinary fileSystem callLibrary (computing)MetadataPointer (computer programming)Data managementEmailInternet service providerBuffer solutionStructural loadLink (knot theory)Type theoryMacro (computer science)Cartesian coordinate systemMultiplication signBuildingVideoconferencingPattern languageMereologyTDMACodeParsingHypermediaComputer fileFunction (mathematics)Limit (category theory)Program slicingDifferent (Kate Ryan album)Vector spaceString (computer science)ChainError messageVariable (mathematics)Data storage deviceUsabilityFile formatWritingObject (grammar)SpacetimePermutationOrder (biology)Electronic mailing listInformation securityVirtual machineRight angleState diagramField (computer science)Process (computing)TrailNumberComputer programmingFreewareIntegerMathematicsEndliche ModelltheoriePlastikkarteMIDIMessage passingGraph coloringDigital electronicsLengthAbstractionOpen setSequenceComputer animation
16:57
Open setOvalTDMAError messageModule (mathematics)Execution unitCloningMacro (computer science)Inclusion mapModal logicArrow of timeRevision controlFlagLine (geometry)Control flowLatin squareMaizeVideoconferencingAnnulus (mathematics)Maß <Mathematik>Extension (kinesiology)Streaming mediaFile formatInformationVideoconferencingBlock (periodic table)Module (mathematics)File formatCodeMacro (computer science)Computer fileEmailData structureCuboidStreaming mediaGame controllerFunctional (mathematics)Revision controlFlagSet (mathematics)Control flowNumberPoint (geometry)Electronic mailing listQueue (abstract data type)Multiplication signBitHydraulic jumpCASE <Informatik>Library (computing)CodecFlash memorySubject indexingProgram slicingReading (process)Closed setType theoryStructural loadOnline helpLatent heatTimestampScripting languageMaxima and minimaMereologyMultiplicationMathematicsShift operatorWord1 (number)2 (number)IntegerParsingSynchronizationTDMAChainComputer animation
25:23
Block (periodic table)Bit rateStrutDuality (mathematics)Plug-in (computing)Different (Kate Ryan album)Computer animation
25:56
File formatInformationBlock (periodic table)Control flowData miningExecution unitMultiplication signINTEGRALGoodness of fitVideoconferencingPhysical systemPlug-in (computing)Message passingCartesian coordinate systemComputer animation
26:41
Physical systemParsingImplementationRepository (publishing)File formatSlide ruleTwitterInformationRight angleBuildingComputer fileLibrary (computing)Repository (publishing)Plug-in (computing)Module (mathematics)CodeFile archiverBitParsingPhysical systemObject (grammar)Dynamical systemProjective planeDifferent (Kate Ryan album)Product (business)Multiplication signFrame problemInterface (computing)VideoconferencingMeeting/InterviewComputer animation
29:49
Integer
Transcript: English(auto-generated)
00:06
I'm Joffre. I'm really happy to be here in Bizberg. It's my first time in Bizberg. I'm going to talk to you about a project that's been going on in my life for quite some time now, and it was the reason I initially came to Rust. So I'm pretty, pretty excited to talk to you about that.
00:23
So, as a few things, I work at CleverCloud, which is a French platform as a service hosting company. So, just kind of like Heroku and others, you get push your code, it runs. We've been doing some Rust a lot this past few months.
00:41
We have actually a lot of code in production that's using Rust. We have replaced big parts of our infrastructure. And I'm really happy to announce that now you can deploy Rust application on our platform. So it's just basically a cargo run on the cloud. Push on as many instances you want. It's in virtual machines.
01:03
It works with Stable. I tested it this morning. It was really, really cool to use. So I'm really, really excited about that because it takes some time to support new platforms, and Rust was so easy to get there. It's amazing. So, let's talk about this little project that I really like, in which I worked for a few years.
01:27
So, VideoLAN makes VLC media player, which is a media player. And the idea of VLC is basically you drag and drop any file and it will work. It should work with almost any format, HTTP, FTP as input or file, MP4, MKV, any codec, anything.
01:51
It should work. So it's a pretty big goal, but it kind of works. But there's an issue with that when you try to handle as many formats as are existing,
02:04
that you've got vulnerabilities. So those are a few ones we got in the past few years. The MP4 parser is very interesting because it's not very complex as a format. It's very, very ambiguous, very hard to get right because there are lots of competing implementations.
02:26
So, VLC is made in C. It's like hundreds, thousands of lines of C everywhere. And all of this, all of the parsing code is written in C. So, we get manual parsers where people just try to interpret the specification, do pointer arithmetic by hand.
02:47
We have formats that are very, very ambiguous, very complex to get right. And so, at some point, I thought maybe there is a better solution for writing parsers in VLC and make the whole application safer.
03:00
So that's when I went looking for something like, you see where I'm going? I wanted something that's memory safe, in which you can write correct code easily, and that you can embed in C because otherwise, I tried to propose Haskell at some point, and they said it was not a good idea to have a garbage-collected language in media replication.
03:21
It could have been cool, really. And that can work in a string. You got data coming and coming and coming, and you have to parse that very, very fast. Because you should not block the rest of the decoding. So, I already have one thing. We have memory safe stuff.
03:42
Can we get good parsers with Rust? Well, yes, but I don't know if you've been going to the Fuzzing workshop yesterday, but even if you use Rust, you will get crashes in parser when you write manual parsers. Because it's still hard to get right. So that's why I started to work on this little project called Norm,
04:05
with the idea that it should be easy to write a good parser, and it should be a fun process. So, Norm basically is two things. It's the approach parsing called the parser combinators.
04:20
The idea is that you have a lot of small functions, very small, very testable, like you can test any part of your parser. And you combine them in larger parser, in larger recognizer, and then you get the whole format. And to do that, there are different approaches to parsing in Rust,
04:42
and long story short, when I started, Rust was very, very young, and macros were a very, very good solution for that, and I think they are still. But you will see how weird it can get. So, basically Rust, Norm, those are just functions, one that take an input type, an output type, and an error type.
05:04
And when you get input into a function, it will get ever incomplete to say, okay, I need more data, which is very useful when you're doing streaming, like maybe you need to fill up the buffer a bit more, an error. And Norm contains the remaining input, what has not been consumed,
05:22
and the output data. So it's really simple, all of the non-passers have this format, this is why I can combine them very easily, because they all follow the same type. So, yeah, macros are hard, right? Yeah, not so much.
05:41
This is a parser that will recognize alphabetic characters that are terminated by digit characters, and will return the alphacast. And this will make a function that takes byte slice as input and returns a byte slice. So it's quite easy to write.
06:02
When you expand that, the macro generates a function that takes, as I said, byte slice as input, byte slice as output, with the same lifetime and everything, so like, I only get slice of the input data, I don't copy anything, zero copy passing, yeah, fast.
06:21
And basically, it's just a big list of pattern matching. So, it's not so bad, right? Actually, it looks not like that. But it's not that bad as well. It just needs to be a bit more, should I say, explicit in the way it does the pattern matching.
06:44
Lots of interesting features. So, it works on byte slice, it works on strings, on bitstreams. So, like, you have a format that works on an even, an aligned size of a bit, like someone asked a few weeks ago,
07:01
I want to parse a list of 11-bit integers. Yeah, Bitcoin. Okay, and you have the combinators, so, like, there was just before terminated, parse the first thing, parse the second, return the results for the first. And there are lots of different combinators,
07:21
like, many will try to apply again and again and again, and return a vector of the results. Pair will generate a tuple with first result, second result. Pick will take, will apply parser on the input, see if it works correctly, but will not consume anything.
07:41
Just takes a look at the data and say, okay, it's all right, it's what I recognize. I use the regex create, so we can put regex in some parsers. And there's some pretty cool error management stuff. So, like, here, basically, there was this input,
08:02
it's from an mp4 file, and I got some name of parsers, and I see which one was applied on which part of the input. So this is something you can do in GNOME, and it's really, really usable. It works on all Rust versions.
08:22
No syntax extension, no import trait. I would have really liked to have import trait when I started GNOME, but it was, I don't know, Rust 0.8, 9, it was really, really hard. There's no STD. It can work on par with C parsers. Like, if you want, you can micro-optimize your parser,
08:43
and it will still be safe, and it can be as fast as C parsers. It's really, really interesting to do that. It works with streaming, and, as I say, it's just function, you can write your own, it's very easy. Now, I'm doing GNOME 2.0. I'm breaking a lot of stuff,
09:01
and I'm adding a lot of features. Whitespace parsing, so you just wrap with ws macro, and it will interspace space parsers between everything, so this is for JSON parser, so it will try to parse a space, then a string, then a space, then a tag, then a space, then a value, then a space, and you don't have to write space everywhere,
09:22
because it was really, really annoying to do, so now it's automatic with that. There's a permutation parser, so you have a list of parser, and you can apply them in any order, as long as all of the results are there, and it will return the results in the order you specified them, so it's really, really cool to use
09:40
for some formats like PNG, and I have just a new syntax for those who used GNOME before. There's the chain stuff, which is very ugly to apply parsers in sequence, so I have do parse, which can apply parser, take the results of parser, and store it in a variable that's usable elsewhere, and that you can even return, so here we recognize the 42 integer,
10:05
and then we take the length, which is in our byte, and we take that many bytes, and we return those bytes. It's a very common pattern in binary formats to have a tag that you recognize, and then the length, and then how many bytes you have to take.
10:21
And after that, a few things. We have custom input types, so no more byte slice or string limits. You can do that on anything else, and you can work with ropes, where you have a structure that has a contiguous buffer abstraction, but it's really a lot of different buffers, so GNOME can work with that, and we can do some pretty vector stuff with this.
10:45
And that's it. Yeah, case-independent parsing, and big, big performance gain for some parsers, because I simplified your error management if you don't need to do all of the interesting stuff where you want to know exactly which parser
11:02
got to which part of the input. Most people don't really need that, so I got a really simple way to get faster parser. So let's get back to the meat of the problem. VLC. So do you know how a media player works? Basically, there's a common pattern of the pipe.
11:23
You have the access. You get a file. You open the file, or you open a network feed. You pass through the demuxer. The demuxers are the parsers, and you get multiple streams, like an audio stream, a video stream, a subtitle, or anything else, and you send them to decoders.
11:41
The decoders will generate data that can be filtered, like you can have post-processing stuff on the video, grayscale, or even scaling, exactly. Then you send them to the output of your machine, or you re-encode, and you can max the stream back to another file or another network feed,
12:04
and you can send that back. Most media players work like this with just a pipe of stuff. The hard thing in that is synchronizing everything. You have the audio and the video and the subtitle stream, and they all go at the same time, but they don't decode as fast.
12:22
It's faster to decode audio than video, and in the end you have to get everything at the same time when you present to the user. Sometimes we get that right, sometimes we don't. The way VLC is made is you have applications like VLC,
12:41
VLMC, which is video editing software. They call into libvlc, which is a nice usable interface to build media players, and libvlc-core is the manager which does everything like load modules, provide IO access,
13:01
synchronizing everything, and all of the modules are linked to libvlc-core for common features. They're all DLL, like libvlc, libvlc-core, and all of the VLC modules, they're all dynamic libraries, and the module is linked to libvlc-core for the features, and libvlc-core loads all of the library to see how they work.
13:23
So to make a module in Rust, we have to do a bit of dance with the way DLLs are loaded. A module is dynamic library loaded by libvlc-core. They have to expose three functions,
13:40
because libvlc-core just tries to load the library, take that function, call it, it exposes some metadata on the module, and then it knows what the module can do. So the plan to start parsing stuff with Rust in VLC is to make a DLL in Rust that acts just like a CDLL,
14:04
you can load directly in the program, you can build with cargo, and that will work just like a C module in VLC. So take the headers, reproduce the stuff we need, link to libvlc-core, get the functions we need,
14:20
reproduce the module stuff, start writing a parser, because that's kind of why I'm here, and well, start parsing stuff. So VLC is written in C, with the VLC command members, this kind of macro stuff in C. We'll always come back to macros, I think.
14:42
And this kind of object-like interface in VLC where there's a common inheritance stuff, so VLC command members see that, like I inherit some attributes from the VLC object.
15:00
Lots of things that can be a bit hard to represent, like the union types and nested structures and everything. So the first thing you have to do is try to write that in Rust. So if you were at Rust when I was talking about that, I said bindgen just failed on the VLC headers.
15:23
Since then, I tried with several bindgen, and it's able to handle the VLC headers, so I'll soon be able to generate the whole bindings like that with bindgen. It should be a lot easier to do. But in the meantime, I wrote those by hand. Basically, you make Rust struct
15:41
that kind of acts like C structs, with pointers everywhere. And you try to make that a bit safer. So you import functions, like this one, if you want to take a stream and get a buffer of a specific size, you wrap those in functions that you can use from Rust correctly.
16:01
When you try to do FFI, you want to isolate all of the unsafe parts, because you don't want to sprinkle unsafe everywhere in your code. So a bit of work to get the FFI running, like all of the structures I need, it was like a stream T reference, another struct with a reference, another thing,
16:21
like you saw in there, that if I want to have the VLC object T, I need to have, you know, if I want to have the DMXT, I need to have the lead VLC and T, the VLC object T, et cetera. So lots of preliminary work. And then we can start writing our module.
16:41
We have the interface, we can import code. And this is how you would declare a module in VLC. Again, it's a macro. To say, okay, this is the name of my module, this is what it can do, it can take and input stuff, and you can pass it.
17:00
And here are two functions, open and close, that you can use to interact with that module. And leave VLC code just takes that and loads the module afterwards, knows what to do with that module. So when you expand that, it's got some C code that's a bit annoying to write.
17:21
So yeah, basically it's just writing code. Let's just write that in Rust. It's easy to write, it's very, very easy to read, right? Uh... No, wait. Macro that stuff. Yeah, I think there's a common theme there
17:40
that I really like macros. I really can't help it. But in that case, it's really useful to do. So as you see, I declare the function VLC entry, like the way it's done in C, you have the VLC entry function. There, okay.
18:00
So this is how the VLC module will be loaded. And from there, okay, you can start passing stuff. Flash video. It's a format that's very simple, like it took me less than two hours to write most of the format. It got audio, video, a few codecs that are mostly outdated.
18:25
And here's the beginning of the format, like you have F, L, and V, the ASCII chars at the beginning, then a version number, then some flags to indicate if you have audio, video, and an offset that indicates when the video and audio streams start in the file.
18:43
It's interesting because you can have the headers, and the offset points to verifying the file, and you can add some stuff between the header and the data. There has been some very, very funny stuff done like that.
19:00
So yeah, I said earlier, okay, there was chain in number one. Now we have to pass, which is a bit easier to use. So VLC, when it gets a file, it will try to call your open method in your DMXer and say, okay, I have some data.
19:21
Tell me if you can pass that. So the first thing is you do a stream peak. You get, okay, I need nine bytes from the beginning of the data, and I will tell you if it's something I can pass. And then I call on that slice the header function that we just designed here,
19:42
and if it passed correctly, it returns a header, and we can say to the file, okay, now I know I can pass that stuff, and we do a stream sync to go to the offset we want. So it's very easy to interact with VLC like that, and so since we know we can be the DMXer for that,
20:01
we pass the DMX and control functions to VLC to say, okay, those are the callbacks you need to use, so call me again and again and again until I've passed all the data. So when you reach that offset, most of the video formats,
20:20
they have multiple streams in multiple blocks, like we'll have one block of audio, one block of video, one block of subtitles, et cetera. That will be interspersed depending on how the encoder did the stuff. There's something for MP4 files where if you took a look at the way people were writing them at the beginning, they put just at the end of the file
20:42
the header you need to see, so you have to go to the end of the stream to do anything, and now they saw that you can mess with the spec and put that header just at the beginning of the file, and it will not allow anybody, and so lots of very weird stuff in video format. Everybody has good ideas about how
21:02
a video format should be, and they're all wrong, basically. I can't criticize anything, but if I design my own, I do something shitty as well. Something interesting there,
21:21
a structure in Rust, and I put it in a box, and I'll be given back that structure when I'm called afterwards, because I cannot want to store some data for my demuxer, and this is the way I interact with VDC. So when I get to the first block,
21:42
I decode a tag that says, okay, it's audio, it's video or script, and I have the size of the data, and a few things like the timestamp indicates when I need to present that part of the data. So if it's audio, it's that second one, and if it's audio, it's that second one, I know I have to present both of them at the same time.
22:04
Okay, so I declare my demux function, which is kind of the way you would do it in C. I read my header, so it's 15 bytes. If I have four bytes, I know it's the end of the stream because I have just a beginning saying
22:22
the tag type and the size, and then I will try to parse everything. So this is how audio header works. So you would have, in the tag you have first the type, then the data size and everything,
22:41
and then you have the format-specific data. For the audio header, it's one byte and says the kind of codec you have, the size and everything. So bitstream parsing in GNOME. You can take your input, which is backslash, you apply bits, which transforms that to base slice and an index,
23:02
and you go through that thing. So here you take four bits, and it gives you an integer that you can use to see which kind of audio codec it is. And then you use that, so you take one byte with stream read,
23:22
and then you parse, and then you have enough data to know, okay, I have this block of data size minus the header one that contains audio data that I can push to VLC. So the interesting thing in there is Rust never owns the data.
23:44
I can ask VLC for some data, but I don't manage its lifetime. I'm not the one allocating, I'm not the one deallocating. I'm just borrowing data from C and trying to not break stuff. It's very important because most of the time when you interact with C code,
24:01
the C code thinks it knows better. And I don't agree with that, but we have to play nice. And don't worry about the unsafe stuff. It's because there are some functions that take VA list, VR, ARGs, stuff, and we had to mess a bit with
24:22
how functions were declared in Rust. So this is really simple because in that parser, I only read the headers. I take the file header, I jump to the next block, I get the block header, which is like 10 bytes if it's audio,
24:42
and then I say, okay, there's this much data you can use, and I'll be called again with the next block. So I never read much data like how we've known in this. So it's quite fast. So don't take my word for it. I will show that it works. Okay?
25:02
So I really like that GIF. Okay. Maybe I'll be able to put that... Oh, yeah, just... If I get it... The thing is, the more you see that GIF, the more it's funny.
25:24
Okay, okay, stop. Really. So, yeah, I will just build and launch and run and copy that in a VLC installation. And I get... It's a very old advertisement for Zelda in FLV.
25:45
And you see that it's decoded with the Rust plugin there. Yeah, the graphics are pretty outdated. So... Yeah. This is Rust.
26:00
This is Rust in VLC. So it's really interesting because, like, I've been spending... Okay, I moved this. So stop that. Stop watching the GIFs. But spend so much time trying to pass stuff and trying to talk to C and everything,
26:22
and then it works in VLC. The first time you get the video to launch, it's such a good feeling, really. But, yeah, now I have to integrate that, really, in VLC because it was just a plugin that I built separately and that copied just in a VLC installation. So, now, the build system.
26:42
Oh, can we build something with other tools? Because Kargo thinks it knows better how to build everything. And I think it's right. But the other tools know better. And you have to play nice again. So, how does it work? First, you have to check if there's a Kargo on Rust C.
27:01
So this is AutoConf stuff. It's pretty nice. I did not write that. I'm really bad, bad with AutoTools. But it's all right. And then, the idea is that you don't build the dynamic library yourself. You build an object file, the Rust plugin.o, because there's also leap tool, which knows better than you
27:22
how to build a dynamic library. Like, Kargo knows how to build a library for Windows, Mac, Linux, and don't care. It works, but play nice with the build system, play nice with C, play nice with the interface, play nice with the FFI. And it's annoying, but Rust makes it kind of easy to do. So, I'm really happy with that.
27:42
And on this, so, yeah, the build system is the biggest issue. The unsafe APIs, you can walk around them and then get really nice code to use when you do FFI. The parser is the easiest part, and it does not really need to be fast, because video decoding is so much slower than parsing
28:01
that you really don't care about that. And remember that you have to be a nice citizen. You're not on your land. You're using someone else's data, so you have to be nice with the way the C handles it. So, from now, a few things I have to do, I have to see how to seek in a file,
28:21
like move back and forth and everything. I have to put it back in the VLC repository. I can build it now inside VLC, so it's quite cool. Predominating dependency will be something that's very interesting, because with VLC, we have this big archive with all of the libraries we need, and so for Rust, I'd like to have that as well.
28:41
And then to complete the imports, I have to play again a bit with BindGen, and we'll be able to replace some plugins, some VLC plugins with Rust. And you can do that with any C project. It's easy to replace a C project file by file. Well, easy, for different reasons,
29:02
different definitions of easy, but it's doable. It's really doable. So, a few people that helped me, Guillaume Gomez, maybe you talked a bit with him about documentation. Luca Babato, who works at LibAV, on the LibAV project, and he helped me making this.
29:21
So, thank you. That's everything you need. If you want to make your own module, here's the library, I extracted it from the project, and you'll see the module is there. Play with Rust, play with C. Deploy C in production, it's cool. Sorry, deploy Rust in production. Do not deploy C in production, please.
29:43
Because then I have to break it. Okay, thank you.