Observation is a Start
There's much knowledge in between your observation ("color changes a little bit") and the "whys" your observation manifests. Seeing as how this is a Blender forum, and most folks are intermediate to advanced, I will delve into some of the whys with the hope that you expand your colour knowledge and help to educate others.
First, unless we have done some dark alchemy via OpenColorIO, our image will be exiting Blender as an RGB based tri-colour model. So factoid number one: RGB is a relative colour system. But what does that mean?
If we take a flash light and cut some plastic of red, blue, and green transparency, we could cover our flash light with the plastic. This would deliver a coloured light of course. But what colour? That is precisely the question to ask when someone starts speaking in terms of values like 0xE3E5E7, which is nothing more than a hex value representing a numerical value relative to the RGB colour space in question. In this case, Blender assumes sRGB / 709 primaries. That is, the absolute colours referenced are relative to the sRGB or 709 specification. If our colour spaces are not aligned, despite having identical RGB values, the colours we look at will not be the same.
When we render the colours to a codec format, in most instances we are performing a transformation of the data into the codec model's format. Typically, most codecs will store data in a very different model compared to RGB, often being YCbCr. The YCbCr model extracts / isolates the luma from the curved RGB values and stores it in Y. The Cb and Cr values then represent two axes that stretch along yellow-blue (Cb) and green-red (Cr). A very different model with a unique transformation.
Second, the codec format typically requires a scaling of the ranges to meet broadcast specification standards. This is nothing more than shrinking the values to a smaller range via a scale, and then offsetting the values slightly.
Third, due to the human eye generally being less receptive to colour as compared to intensity, a codec will often scale down the Cb and Cr channels to make the codec smaller in size.
Fourth, the codec will likely compress all of this data even further using some form of lossy encoding standard, although it may also be lossless.
When Encoding Doesn't Match Decoding
Needless to say, the above steps outline a varying number of potentially complex transformations to get your RGB data out of the RGB model and into the YCbCr model. If any one of those steps is incorrectly handled by encoding or decoding, your results will vary quite dramatically.
So the simple question is "Given all of the above maze-like transforms that happen, can I get anything close to my original RGB image?" and the answer is unequivocally "Yes!" Can you do it with a simple click of a Blender render button? The chances are likely "No."
Let's assume we do a perfect encode of our material, what then? Then we are relying on the exact inverse of all of the above transformations to end up back with our perfect RGB values1. As you can guess, this only happens when an artist checks their work and the player is perfect. Even then, to achieve real-time playback, a piece of software may hand off the YCbCr stream to the graphics card, and you guessed it, they will often decode things incorrectly for any number of reasons.
If you are interested in achieving a perfect encode, the best advice is become a knowledgeable artist in the realm of colour and pixels. You have taken the first steps in noticing that things aren't quite right.
If you encode to a series of images and then encode again to your codec, you stand a much better chance of testing your pipeline for accuracy. In particular, because most encoding software may have different design constraints2, they may make different design decisions that result in different results. Therefore, the safest way to handle video is to encode the YCbCr yourself and then pass that raw data to an encoder and test the encoded values.
If they match, it is out of your hands and into your audience's set of qualitative evaluations at their end to decode it, if this is your goal. If they don't, something went wrong and you can correct it.
The above is a quick outline as to the sad reality of the complexities of your initial, seemingly simple first observation. Controlling the output on the other hand, is relatively easy and will get faster and faster with more knowledge. This is likely beyond the scope of this Stack Exchange answer though, so I'll leave it at that3.
- Quantization errors may be present depending on the many encoding options you choose. Broadcast scaling for example, will necessarily degrade your image due to a reduced number of bits storage.
- Many video encoders and decoders may target speed of encoding or decoding for quick encoding or speedy playback. Of course this will come at a cost, often times being accuracy due to lower bit depths used to encode or short cuts to achieve playback speed.
- Feel free to contact me if you want to explore this area. It is possible to encode your data to a bitwise precise encode, subject to quantization / specifications. I should be relatively easy to find.