diff --git a/src/main/java/cz/it4i/qcmp/cache/SqQvcFile.java b/src/main/java/cz/it4i/qcmp/cache/SqQvcFile.java
index 5071ef2369755ab6c6f3ce948ad945b17f898e43..4364729243ae1b8e3365641812bca046f3da6908 100644
--- a/src/main/java/cz/it4i/qcmp/cache/SqQvcFile.java
+++ b/src/main/java/cz/it4i/qcmp/cache/SqQvcFile.java
@@ -1,6 +1,12 @@
 package cz.it4i.qcmp.cache;
 
 import cz.it4i.qcmp.fileformat.IQvcHeader;
+import cz.it4i.qcmp.fileformat.QvcHeaderV2;
+import cz.it4i.qcmp.huffman.HuffmanNode;
+import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
+import cz.it4i.qcmp.io.InBitStream;
+import cz.it4i.qcmp.io.MemoryOutputStream;
+import cz.it4i.qcmp.io.OutBitStream;
 import cz.it4i.qcmp.quantization.scalar.SQCodebook;
 
 import java.io.DataInputStream;
@@ -22,34 +28,66 @@ public class SqQvcFile implements IQvcFile {
 
     @Override
     public void writeToStream(final DataOutputStream outputStream) throws IOException {
-        // TODO
+        assert (header instanceof QvcHeaderV2) : "Only the latest header is supporter when writing qvc file.";
+
+        final int huffmanTreeBinaryRepresentationSize;
+        final MemoryOutputStream bufferStream = new MemoryOutputStream(256);
+        try (final OutBitStream bitStream = new OutBitStream(bufferStream, header.getBitsPerCodebookIndex(), 32)) {
+            codebook.getHuffmanTreeRoot().writeToBinaryStream(bitStream);
+            huffmanTreeBinaryRepresentationSize = (int) bitStream.getBytesWritten();
+        }
+        assert (huffmanTreeBinaryRepresentationSize == bufferStream.getCurrentBufferLength());
+        ((QvcHeaderV2) header).setHuffmanDataSize(huffmanTreeBinaryRepresentationSize);
+
         header.writeToStream(outputStream);
+        
         final int[] quantizationValues = codebook.getCentroids();
-        final long[] frequencies = codebook.getSymbolFrequencies();
-
         for (final int qV : quantizationValues) {
             outputStream.writeShort(qV);
         }
-        for (final long sF : frequencies) {
-            outputStream.writeLong(sF);
-        }
+
+        outputStream.write(bufferStream.getBuffer(), 0, huffmanTreeBinaryRepresentationSize);
     }
 
+    /**
+     * Read codebook from file based on format version.
+     *
+     * @param inputStream Input stream.
+     * @param header      File header.
+     * @throws IOException when fails to read from input stream.
+     */
     @Override
     public void readFromStream(final DataInputStream inputStream, final IQvcHeader header) throws IOException {
-        // TODO
         this.header = header;
+
+        final int headerVersion = header.getHeaderVersion();
         final int codebookSize = header.getCodebookSize();
-        final int[] centroids = new int[codebookSize];
-        final long[] frequencies = new long[codebookSize];
 
+        final int[] centroids = new int[codebookSize];
         for (int i = 0; i < codebookSize; i++) {
             centroids[i] = inputStream.readUnsignedShort();
         }
-        for (int i = 0; i < codebookSize; i++) {
-            frequencies[i] = inputStream.readLong();
+        final HuffmanNode huffmanRoot;
+        if (headerVersion == 1) {           // First version of qvc file.
+            final long[] frequencies = new long[codebookSize];
+            for (int i = 0; i < codebookSize; i++) {
+                frequencies[i] = inputStream.readLong();
+            }
+
+            final HuffmanTreeBuilder builder = new HuffmanTreeBuilder(codebookSize, frequencies);
+            builder.buildHuffmanTree();
+            huffmanRoot = builder.getRoot();
+        } else if (headerVersion == 2) {    // Second version of qvc file.
+            final InBitStream bitStream = new InBitStream(inputStream,
+                                                          header.getBitsPerCodebookIndex(),
+                                                          ((QvcHeaderV2) header).getHuffmanDataSize());
+            bitStream.fillEntireBuffer();
+            bitStream.setAllowReadFromUnderlyingStream(false);
+            huffmanRoot = HuffmanNode.readFromStream(bitStream);
+        } else {
+            throw new IOException("Unable to read SqQvcFile of version: " + headerVersion);
         }
-        codebook = new SQCodebook(centroids, frequencies);
+        codebook = new SQCodebook(centroids, huffmanRoot);
     }
 
     @Override
diff --git a/src/main/java/cz/it4i/qcmp/cache/VqQvcFile.java b/src/main/java/cz/it4i/qcmp/cache/VqQvcFile.java
index 13104c43a9f12e76d1cbd9a0f88b3737206b3dd4..5fcd635afd1e2b9fdcd5bf019ddf9f82e5624017 100644
--- a/src/main/java/cz/it4i/qcmp/cache/VqQvcFile.java
+++ b/src/main/java/cz/it4i/qcmp/cache/VqQvcFile.java
@@ -1,6 +1,12 @@
 package cz.it4i.qcmp.cache;
 
 import cz.it4i.qcmp.fileformat.IQvcHeader;
+import cz.it4i.qcmp.fileformat.QvcHeaderV2;
+import cz.it4i.qcmp.huffman.HuffmanNode;
+import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
+import cz.it4i.qcmp.io.InBitStream;
+import cz.it4i.qcmp.io.MemoryOutputStream;
+import cz.it4i.qcmp.io.OutBitStream;
 import cz.it4i.qcmp.quantization.vector.VQCodebook;
 
 import java.io.DataInputStream;
@@ -22,7 +28,17 @@ public class VqQvcFile implements IQvcFile {
 
     @Override
     public void writeToStream(final DataOutputStream outputStream) throws IOException {
-        // TODO
+        assert (header instanceof QvcHeaderV2) : "Only the latest header is supporter when writing qvc file.";
+
+        final int huffmanTreeBinaryRepresentationSize;
+        final MemoryOutputStream bufferStream = new MemoryOutputStream(256);
+        try (final OutBitStream bitStream = new OutBitStream(bufferStream, header.getBitsPerCodebookIndex(), 32)) {
+            codebook.getHuffmanTreeRoot().writeToBinaryStream(bitStream);
+            huffmanTreeBinaryRepresentationSize = (int) bitStream.getBytesWritten();
+        }
+        assert (huffmanTreeBinaryRepresentationSize == bufferStream.getCurrentBufferLength());
+        ((QvcHeaderV2) header).setHuffmanDataSize(huffmanTreeBinaryRepresentationSize);
+
         header.writeToStream(outputStream);
 
         final int[][] entries = codebook.getVectors();
@@ -32,33 +48,45 @@ public class VqQvcFile implements IQvcFile {
             }
         }
 
-        final long[] frequencies = codebook.getVectorFrequencies();
-        for (final long vF : frequencies) {
-            outputStream.writeLong(vF);
-        }
+        outputStream.write(bufferStream.getBuffer(), 0, huffmanTreeBinaryRepresentationSize);
     }
 
     @Override
     public void readFromStream(final DataInputStream inputStream, final IQvcHeader header) throws IOException {
-        // TODO
         this.header = header;
         final int codebookSize = header.getCodebookSize();
 
         final int entrySize = header.getVectorDim().multiplyTogether();
         final int[][] vectors = new int[codebookSize][entrySize];
-        final long[] frequencies = new long[codebookSize];
 
         for (int i = 0; i < codebookSize; i++) {
-            //int[] vector = new int[entrySize];
             for (int j = 0; j < entrySize; j++) {
                 vectors[i][j] = inputStream.readUnsignedShort();
             }
         }
 
-        for (int i = 0; i < codebookSize; i++) {
-            frequencies[i] = inputStream.readLong();
+        final HuffmanNode huffmanRoot;
+        final int headerVersion = header.getHeaderVersion();
+        if (headerVersion == 1) {
+            final long[] frequencies = new long[codebookSize];
+            for (int i = 0; i < codebookSize; i++) {
+                frequencies[i] = inputStream.readLong();
+            }
+            final HuffmanTreeBuilder builder = new HuffmanTreeBuilder(codebookSize, frequencies);
+            builder.buildHuffmanTree();
+            huffmanRoot = builder.getRoot();
+        } else if (headerVersion == 2) {
+            final InBitStream bitStream = new InBitStream(inputStream,
+                                                          header.getBitsPerCodebookIndex(),
+                                                          ((QvcHeaderV2) header).getHuffmanDataSize());
+            bitStream.fillEntireBuffer();
+            bitStream.setAllowReadFromUnderlyingStream(false);
+            huffmanRoot = HuffmanNode.readFromStream(bitStream);
+        } else {
+            throw new IOException("Unable to read VqQvcFile of version: " + headerVersion);
         }
-        codebook = new VQCodebook(header.getVectorDim(), vectors, frequencies);
+
+        codebook = new VQCodebook(header.getVectorDim(), vectors, huffmanRoot);
     }
 
     @Override
diff --git a/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV2.java b/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV2.java
index c5d759c06717b2cfcdb1ecb02cef0bc57b120ac8..a8e6e36113018fba3aaab267b01ea840148f1620 100644
--- a/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV2.java
+++ b/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV2.java
@@ -1,6 +1,7 @@
 package cz.it4i.qcmp.fileformat;
 
 import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
 
 public class QvcHeaderV2 extends QvcHeaderV1 {
@@ -12,7 +13,7 @@ public class QvcHeaderV2 extends QvcHeaderV1 {
     //endregion
 
     //region Header fields.
-    int huffmanDataSize;
+    private int huffmanDataSize;
     //endregion
 
     //region IFileHeader implementation
@@ -40,6 +41,17 @@ public class QvcHeaderV2 extends QvcHeaderV1 {
             throw new IOException("Unable to read QvcHeaderV2. Unable to skip reserved bytes.");
     }
 
+    @Override
+    public void writeToStream(final DataOutputStream outputStream) throws IOException {
+        super.writeToStream(outputStream);
+
+        outputStream.writeShort(huffmanDataSize);
+
+        for (int i = 0; i < RESERVED_BYTES_SIZE; i++) {
+            outputStream.writeByte(0);
+        }
+    }
+
     @Override
     public long getExpectedDataSize() {
         long expectedFileSize = BASE_HEADER_SIZE + trainFileNameSize + huffmanDataSize;
@@ -65,4 +77,13 @@ public class QvcHeaderV2 extends QvcHeaderV1 {
         sb.append("HuffmanDataSize: ").append(huffmanDataSize).append('\n');
     }
     //endregion
+
+
+    public int getHuffmanDataSize() {
+        return huffmanDataSize;
+    }
+
+    public void setHuffmanDataSize(final int n) {
+        huffmanDataSize = n;
+    }
 }
diff --git a/src/main/java/cz/it4i/qcmp/quantization/Codebook.java b/src/main/java/cz/it4i/qcmp/quantization/Codebook.java
index 66f8c296fdec23a4981c207045f14e298afeab7f..f894dacb384be3c6b447e43067ac18ebe21a835c 100644
--- a/src/main/java/cz/it4i/qcmp/quantization/Codebook.java
+++ b/src/main/java/cz/it4i/qcmp/quantization/Codebook.java
@@ -47,4 +47,13 @@ public class Codebook {
             huffmanEncoder = new HuffmanEncoder(huffmanRoot, HuffmanTreeBuilder.createSymbolCodes(huffmanRoot));
         return huffmanEncoder;
     }
+
+    /**
+     * Get root of the huffman tree coder.
+     *
+     * @return Root of the huffman tree.
+     */
+    public HuffmanNode getHuffmanTreeRoot() {
+        return huffmanRoot;
+    }
 }