diff --git a/components/codecs/include/ftl/codecs/bitrates.hpp b/components/codecs/include/ftl/codecs/bitrates.hpp
index fbacb49790577d8d354e9acff69b275834803a1e..eeb857c82d5dbcfa2348c8607ba131e98c507040 100644
--- a/components/codecs/include/ftl/codecs/bitrates.hpp
+++ b/components/codecs/include/ftl/codecs/bitrates.hpp
@@ -13,8 +13,8 @@ namespace codecs {
 enum struct codec_t : uint8_t {
 	JPG = 0,
 	PNG,
-    H264,
-    HEVC,  // H265
+	H264,
+	HEVC,  // H265
 
 	// TODO: Add audio codecs
 	WAV,
diff --git a/python/ftl/ftlstream.py b/python/ftl/ftlstream.py
index 6031e757d39b915f76db997952f17a3d77ffebd0..c3790ac5491d2e3b5c439a7539ecbd57d5694c7c 100644
--- a/python/ftl/ftlstream.py
+++ b/python/ftl/ftlstream.py
@@ -12,8 +12,6 @@ from . misc import is_iframe
 from . import ftltypes as ftl
 from . import libde265
 
-_calib_fmt = "@ddddIIdddd"
-
 try:
     import cv2 as cv
     
@@ -27,7 +25,7 @@ except ImportError:
         ''' YCrCb to RGB, based on OpenCV documentation definition.
         
         Note: It seems this implementation is not perfectly equivalent to
-        OpenCV's
+        OpenCV's (results not exactly same, why?)
         '''
         
         rgb = np.zeros(img.shape, np.float)
@@ -43,11 +41,25 @@ except ImportError:
 
         return rgb.round().astype(np.uint8)
 
+def _ycbcr2rgb(img):
+    rgb = np.zeros(img.shape, np.float)
+
+    Y = img[:,:,0].astype(np.float)
+    Cr = img[:,:,2].astype(np.float)
+    Cb = img[:,:,1].astype(np.float)
+    delta = 128.0
+
+    rgb[:,:,0] = Y + 1.403 * (Cr - delta)
+    rgb[:,:,1] = Y - 0.714 * (Cr - delta) - 0.344 * (Cb - delta)
+    rgb[:,:,2] = Y + 1.773 * (Cb - delta)
+
+    return rgb.round().astype(np.uint8)
+
 class FTLStreamWriter:
-    def __init__(self, file):
+    def __init__(self, file, version=2):
         self._file = open(file, "wb")
         self._file.write(bytes(ord(c) for c in "FTLF")) # magic
-        self._file.write(bytes([2]))                    # version
+        self._file.write(bytes([version]))              # version
         self._file.write(bytes([0]*64))                 # reserved
 
         self._packer = msgpack.Packer(strict_types=False, use_bin_type=True)
@@ -94,7 +106,7 @@ class FTLStreamWriter:
             if codec == ftl.codec_t.PNG:
                 if ftl.is_float_channel(channel):
                     # scaling always same (???)
-                    data = data.astype(np.float) / 1000.0
+                    data = (data * 1000).astype(np.uint16)
                 
                 params = [cv.IMWRITE_PNG_COMPRESSION, 9]
                 retval, data = cv.imencode(".png", data, params)
@@ -134,7 +146,8 @@ class FTLStreamWriter:
         raise NotImplementedError("todo")
 
     def add_calibration(self, timestamp, source, data):
-        struct.pack(_calib_fmt, *data)
+        # todo: Use msgpack format instead (ftlf v3+)
+        struct.pack("@ddddIIdddd", *data)
         raise NotImplementedError("todo")
 
 class FTLStreamReader:
@@ -180,8 +193,18 @@ class FTLStreamReader:
     
     def _update_calib(self, sp, p):
         ''' Update calibration. '''
-        calibration = struct.unpack(_calib_fmt, p.data[:(4*8+2*4+4*8)])
-        self._calibration[sp.streamID] = ftl.Camera._make(calibration)
+        
+        if p.codec == ftl.codec_t.MSGPACK:
+            # TODO: channel and capabilities should be saved as well
+            calib, channel, capabilities = msgpack.unpackb(p.data)
+            self._calibration[sp.streamID] = ftl.Camera._make(calib)
+        
+        elif p.codec == ftl.codec_t.CALIBRATION:
+            calibration = struct.unpack("@ddddIIdddd", p.data[:(4*8+2*4+4*8)])
+            self._calibration[sp.streamID] = ftl.Camera._make(calibration)
+        
+        else:
+            raise Exception("Unknown codec %i for calibration" % p.codec)
 
     def _update_pose(self, sp, p):
         ''' Update pose '''
@@ -226,7 +249,10 @@ class FTLStreamReader:
             raise NotImplementedError("non-color channel decoding not available")
         
         else:
-            self._frame = _ycrcb2rgb(img)
+            if self._version < 3:
+                self._frame = _ycrcb2rgb(img)
+            else:
+                self._frame = _ycbcr2rgb(img)
 
     def _decode_opencv(self, sp, p):
         try:
@@ -254,30 +280,31 @@ class FTLStreamReader:
         Reads data for until the next timestamp. Returns False if there is no
         more data to read, otherwise returns True.
 
-        todo: make decoding optional
+        todo: make (frame) decoding optional
         '''
         self._frame = None
-
+        
         try:
             self._sp, self._p = self._read_next()
             self._packets_read += 1
         
         except msgpack.OutOfData:
             return False
-
+        
         if self._p.block_total != 1 or self._p.block_number != 0:
             raise Exception("Unsupported block format (todo)")
 
-        if self._p.codec == ftl.codec_t.JSON:
-            self._process_json(self._sp, self._p)
-
-        elif self._p.codec == ftl.codec_t.CALIBRATION:
+        # calibration/pose cached
+        # todo: should be done by user instead?
+        
+        if self._sp.channel == ftl.Channel.Calibration:
             self._update_calib(self._sp, self._p)
 
-        elif self._p.codec == ftl.codec_t.POSE:
+        elif self._sp.channel == ftl.Channel.Pose:
             self._update_pose(self._sp, self._p)
 
-        elif self._p.codec == ftl.codec_t.HEVC:
+        # decode if codec supported
+        if self._p.codec == ftl.codec_t.HEVC:
             self._decode_hevc(self._sp, self._p)
 
         elif self._p.codec == ftl.codec_t.PNG:
@@ -287,7 +314,8 @@ class FTLStreamReader:
             self._decode_opencv(self._sp, self._p)
 
         else:
-            raise Exception("unkowno codec %i" % self._p.codec)
+            # todo (unsupported codec)
+            pass
 
         return True
 
@@ -336,7 +364,7 @@ class FTLStreamReader:
             raise ValueError("source id %i not found" % source)
     
     def get_Q(self, source):
-        ''' Disparity to depth matrix in OpenCV format '''
+        ''' Disparity to depth matrix (OpenCV) '''
 
         calib = self.get_calibration(source)
         Q = np.identity(4, dtype=np.float64)
diff --git a/python/ftl/ftltypes.py b/python/ftl/ftltypes.py
index 17980b808fbadd68c1adff9f17a037a92878c27e..69eb872489e92bb460ae3c57365b85993a79f387 100644
--- a/python/ftl/ftltypes.py
+++ b/python/ftl/ftltypes.py
@@ -65,7 +65,9 @@ class codec_t(IntEnum):
     JSON = 100
     CALIBRATION = 101
     POSE = 102
-    RAW = 103
+    MSGPACK = 103,
+    STRING = 104,
+    RAW = 105
 
 definition_t = {
     0 : (7680, 4320),
@@ -85,4 +87,3 @@ def get_definition(shape):
 			return k
 	
 	return 7 # (None)
-