diff --git a/src/native/vpx/org_jitsi_impl_neomedia_recording_WebmWriter.cc b/src/native/vpx/org_jitsi_impl_neomedia_recording_WebmWriter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a654ba2f8730f47c56ee32686b573b87f722448f
--- /dev/null
+++ b/src/native/vpx/org_jitsi_impl_neomedia_recording_WebmWriter.cc
@@ -0,0 +1,478 @@
+#include <assert.h>
+#include <jni.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <new>
+
+#include "vpx/vpx_codec.h"
+#include "vpx/vpx_encoder.h"
+
+extern "C" {
+#include "libmkv/EbmlWriter.h"
+#include "libmkv/EbmlIDs.h"
+}
+#define LITERALU64(n) n##LLU
+
+#ifdef NDEBUG
+# define printf(fmt, ...)
+#else
+# ifdef ANDROID_NDK
+#  include <android/log.h>
+#  define printf(fmt, ...) \
+   __android_log_print(ANDROID_LOG_DEBUG, "LIBVPX_WEBM", fmt, ##__VA_ARGS__)
+# else
+#  define printf(fmt, ...) \
+   printf(fmt "\n", ##__VA_ARGS__)
+# endif
+#endif
+
+#define FUNC(RETURN_TYPE, NAME, ...) \
+  extern "C" { \
+  JNIEXPORT RETURN_TYPE Java_org_jitsi_impl_neomedia_recording_WebmWriter_ ## NAME \
+                      (JNIEnv * env, jobject thiz, ##__VA_ARGS__);\
+  } \
+  JNIEXPORT RETURN_TYPE Java_org_jitsi_impl_neomedia_recording_WebmWriter_ ## NAME \
+                      (JNIEnv * env, jobject thiz, ##__VA_ARGS__)\
+
+#define STRING_RETURN(JNI_NAME, LIBVPX_NAME) \
+  FUNC(jstring, JNI_NAME) { \
+    printf(#JNI_NAME); \
+    return env->NewStringUTF(LIBVPX_NAME()); \
+  }
+
+/* Stereo 3D packed frame format */
+typedef enum stereo_format {
+  STEREO_FORMAT_MONO       = 0,
+  STEREO_FORMAT_LEFT_RIGHT = 1,
+  STEREO_FORMAT_BOTTOM_TOP = 2,
+  STEREO_FORMAT_TOP_BOTTOM = 3,
+  STEREO_FORMAT_RIGHT_LEFT = 11
+} stereo_format_t;
+
+typedef off_t EbmlLoc;
+
+struct cue_entry {
+  unsigned int time;
+  uint64_t     loc;
+};
+
+extern "C" {
+struct EbmlGlobal {
+  FILE *stream;
+  int64_t last_pts_ms;
+
+  /* These pointers are to the start of an element */
+  off_t    position_reference;
+  off_t    seek_info_pos;
+  off_t    segment_info_pos;
+  off_t    track_pos;
+  off_t    cue_pos;
+  off_t    cluster_pos;
+
+  /* This pointer is to a specific element to be serialized */
+  off_t    track_id_pos;
+
+  /* These pointers are to the size field of the element */
+  EbmlLoc  startSegment;
+  EbmlLoc  startCluster;
+
+  uint32_t cluster_timecode;
+  int      cluster_open;
+
+  struct cue_entry *cue_list;
+  unsigned int      cues;
+};
+}
+
+FUNC(jlong, allocCfg) {
+  EbmlGlobal *glob = new (std::nothrow) EbmlGlobal;
+
+  if (glob) {
+    memset(glob, 0, sizeof(*glob));
+    glob->last_pts_ms = -1;
+  }
+
+  return (intptr_t)glob;
+}
+
+FUNC(void, freeCfg, jlong jglob) {
+  const EbmlGlobal *glob = reinterpret_cast<EbmlGlobal*>(jglob);
+
+  if (glob != NULL) {
+    if (glob->stream)
+      fclose(glob->stream);
+
+    free(glob->cue_list);
+
+    delete glob;
+
+    glob = 0;
+  }
+}
+
+FUNC(jboolean, openFile, jlong jglob, jstring fileName) {
+  EbmlGlobal *glob = reinterpret_cast<EbmlGlobal*>(jglob);
+  const char *mfile = env->GetStringUTFChars(fileName, 0);
+
+  glob->stream = fopen(mfile, "wb");
+
+  env->ReleaseStringUTFChars(fileName, mfile);
+
+  if (!glob->stream)
+    return JNI_TRUE;
+
+  return JNI_FALSE;
+}
+
+extern "C" {
+void Ebml_Write(EbmlGlobal *glob, const void *buffer_in, unsigned long len) {
+  if (fwrite(buffer_in, 1, len, glob->stream))
+    ;
+}
+}
+
+#define WRITE_BUFFER(s) \
+  for (i = len - 1; i >= 0; i--) { \
+    x = *(const s *)buffer_in >> (i * CHAR_BIT); \
+    Ebml_Write(glob, &x, 1ULL); \
+  }
+extern "C" {
+void Ebml_Serialize(EbmlGlobal *glob, const void *buffer_in,
+                    int buffer_size, unsigned long len) {
+  char x;
+  int i;
+
+  /* buffer_size:
+    * 1 - int8_t;
+    * 2 - int16_t;
+    * 3 - int32_t;
+    * 4 - int64_t;
+    */
+  switch (buffer_size) {
+    case 1:
+      WRITE_BUFFER(int8_t)
+      break;
+    case 2:
+      WRITE_BUFFER(int16_t)
+      break;
+    case 4:
+      WRITE_BUFFER(int32_t)
+      break;
+    case 8:
+      WRITE_BUFFER(int64_t)
+      break;
+    default:
+      break;
+  }
+}
+}
+#undef WRITE_BUFFER
+
+/* Need a fixed size serializer for the track ID. libmkv provides a 64 bit
+ * one, but not a 32 bit one.
+ */
+static void Ebml_SerializeUnsigned32(EbmlGlobal *glob,
+                                     uint64_t class_id, uint64_t ui) {
+  unsigned char sizeSerialized = 4 | 0x80;
+  Ebml_WriteID(glob, class_id);
+  Ebml_Serialize(glob, &sizeSerialized, sizeof(sizeSerialized), 1ULL);
+  Ebml_Serialize(glob, &ui, sizeof(ui), 4ULL);
+}
+
+static void Ebml_StartSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc,
+                                 uint64_t class_id) {
+  // todo this is always taking 8 bytes, this may need later optimization
+  // this is a key that says length unknown
+  uint64_t unknownLen =  LITERALU64(0x01FFFFFFFFFFFFFF);
+
+  Ebml_WriteID(glob, class_id);
+  *ebmlLoc = ftello(glob->stream);
+  Ebml_Serialize(glob, &unknownLen, sizeof(unknownLen), 8ULL);
+}
+
+static void Ebml_EndSubElement(EbmlGlobal *glob, EbmlLoc *ebmlLoc) {
+  off_t pos;
+  uint64_t size;
+
+  /* Save the current stream pointer */
+  pos = ftello(glob->stream);
+
+  /* Calculate the size of this element */
+  size = pos - *ebmlLoc - 8;
+  size |=  LITERALU64(0x0100000000000000);
+
+  /* Seek back to the beginning of the element and write the new size */
+  fseeko(glob->stream, *ebmlLoc, SEEK_SET);
+  Ebml_Serialize(glob, &size, sizeof(size), 8ULL);
+
+  /* Reset the stream pointer */
+  fseeko(glob->stream, pos, SEEK_SET);
+}
+
+static void write_webm_seek_element(EbmlGlobal *ebml, uint64_t id, off_t pos) {
+  uint64_t offset = pos - ebml->position_reference;
+  EbmlLoc start;
+  Ebml_StartSubElement(ebml, &start, Seek);
+  Ebml_SerializeBinary(ebml, SeekID, id);
+  Ebml_SerializeUnsigned64(ebml, SeekPosition, offset);
+  Ebml_EndSubElement(ebml, &start);
+}
+
+static void write_webm_seek_info(EbmlGlobal *ebml) {
+  off_t pos;
+
+  /* Save the current stream pointer */
+  pos = ftello(ebml->stream);
+
+  if (ebml->seek_info_pos)
+    fseeko(ebml->stream, ebml->seek_info_pos, SEEK_SET);
+  else
+    ebml->seek_info_pos = pos;
+
+  {
+    EbmlLoc start;
+
+    Ebml_StartSubElement(ebml, &start, SeekHead);
+    write_webm_seek_element(ebml, Tracks, ebml->track_pos);
+    write_webm_seek_element(ebml, Cues,   ebml->cue_pos);
+    write_webm_seek_element(ebml, Info,   ebml->segment_info_pos);
+    Ebml_EndSubElement(ebml, &start);
+  }
+  {
+    // segment info
+    EbmlLoc startInfo;
+    uint64_t frame_time = 45; //approx. the duration of a single frame (in ms).
+    char version_string[64];
+
+    /* Assemble version string */
+    strcpy(version_string, "vpxenc ");
+    strncat(version_string,
+            vpx_codec_version_str(),
+            sizeof(version_string) - 1 - strlen(version_string));
+
+    ebml->segment_info_pos = ftello(ebml->stream);
+    Ebml_StartSubElement(ebml, &startInfo, Info);
+    Ebml_SerializeUnsigned(ebml, TimecodeScale, 1000000);
+    Ebml_SerializeFloat(ebml, Segment_Duration,
+                        ebml->last_pts_ms + frame_time);
+    Ebml_SerializeString(ebml, MuxingApp, version_string);
+    Ebml_SerializeString(ebml, WritingApp, version_string);
+    Ebml_EndSubElement(ebml, &startInfo);
+  }
+}
+
+FUNC(void, writeWebmFileHeader, jlong jglob,
+                                            jint width, jint height) {
+  EbmlGlobal *glob = reinterpret_cast<EbmlGlobal*>(jglob);
+  stereo_format_t stereo_fmt = STEREO_FORMAT_MONO;
+
+  EbmlLoc start;
+  Ebml_StartSubElement(glob, &start, EBML);
+  Ebml_SerializeUnsigned(glob, EBMLVersion, 1);
+  Ebml_SerializeUnsigned(glob, EBMLReadVersion, 1);  // EBML Read Version
+  Ebml_SerializeUnsigned(glob, EBMLMaxIDLength, 4);  // EBML Max ID Length
+  Ebml_SerializeUnsigned(glob, EBMLMaxSizeLength, 8);  // EBML Max Size Length
+  Ebml_SerializeString(glob, DocType, "webm");  // Doc Type
+  Ebml_SerializeUnsigned(glob, DocTypeVersion, 2);  // Doc Type Version
+  Ebml_SerializeUnsigned(glob, DocTypeReadVersion, 2);  // Doc Type Read Version
+  Ebml_EndSubElement(glob, &start);
+
+  {
+    Ebml_StartSubElement(glob, &glob->startSegment, Segment);  // segment
+    glob->position_reference = ftello(glob->stream);
+    write_webm_seek_info(glob);
+
+    {
+      EbmlLoc trackStart;
+      glob->track_pos = ftello(glob->stream);
+      Ebml_StartSubElement(glob, &trackStart, Tracks);
+      {
+        unsigned int trackNumber = 1;
+        uint64_t     trackID = 0;
+
+        EbmlLoc start;
+        Ebml_StartSubElement(glob, &start, TrackEntry);
+        Ebml_SerializeUnsigned(glob, TrackNumber, trackNumber);
+        glob->track_id_pos = ftello(glob->stream);
+        Ebml_SerializeUnsigned32(glob, TrackUID, trackID);
+        Ebml_SerializeUnsigned(glob, TrackType, 1);  // video is always 1
+        Ebml_SerializeString(glob, CodecID, "V_VP8");
+        {
+          unsigned int pixelWidth = width;
+          unsigned int pixelHeight = height;
+
+          EbmlLoc videoStart;
+          Ebml_StartSubElement(glob, &videoStart, Video);
+          Ebml_SerializeUnsigned(glob, PixelWidth, pixelWidth);
+          Ebml_SerializeUnsigned(glob, PixelHeight, pixelHeight);
+          Ebml_SerializeUnsigned(glob, StereoMode, stereo_fmt);
+          //Ebml_SerializeFloat(glob, FrameRate, frameRate);
+          Ebml_EndSubElement(glob, &videoStart);  // Video
+        }
+        Ebml_EndSubElement(glob, &start);  // Track Entry
+      }
+      Ebml_EndSubElement(glob, &trackStart);
+    }
+    // segment element is open
+  }
+}
+
+FUNC(void, writeWebmBlock, jlong jglob, jobject jfd) {
+  EbmlGlobal *glob = reinterpret_cast<EbmlGlobal*>(jglob);
+
+  jclass frameDescriptor = env->FindClass("org/jitsi/impl/neomedia/recording/WebmWriter$FrameDescriptor");
+  assert(frameDescriptor != NULL);
+
+  jfieldID bufferId = env->GetFieldID(frameDescriptor, "buffer", "[B");
+  assert(bufferId != NULL);
+
+  jfieldID offsetId = env->GetFieldID(frameDescriptor, "offset", "I");
+  assert(offsetId != NULL);
+
+  jfieldID lengthId = env->GetFieldID(frameDescriptor, "length", "J");
+  assert(lengthId != NULL);
+
+  jfieldID flagsId = env->GetFieldID(frameDescriptor, "flags", "I");
+  assert(flagsId != NULL);
+
+  jfieldID ptsId = env->GetFieldID(frameDescriptor, "pts", "J");
+  assert(ptsId != NULL);
+
+  jobject jba = env->GetObjectField(jfd, bufferId);
+  assert(jba != NULL);
+
+  jint offset = env->GetIntField(jfd, offsetId);
+
+  jint frameFlags = env->GetIntField(jfd, flagsId);
+
+  uint64_t       block_length;
+  unsigned char  track_number;
+  uint16_t       block_timecode = 0;
+  unsigned char  flags;
+  int64_t        pts_ms;
+  int            start_cluster = 0, is_keyframe;
+
+  /* Calculate the PTS of this frame in milliseconds */
+  pts_ms = env->GetLongField(jfd, ptsId);
+  if (pts_ms <= glob->last_pts_ms)
+    pts_ms = glob->last_pts_ms + 1;
+  glob->last_pts_ms = pts_ms;
+
+  /* Calculate the relative time of this block */
+  if (pts_ms - glob->cluster_timecode > SHRT_MAX)
+    start_cluster = 1;
+  else
+    block_timecode = pts_ms - glob->cluster_timecode;
+
+  is_keyframe = (frameFlags & VPX_FRAME_IS_KEY);
+  if (start_cluster || is_keyframe) {
+    if (glob->cluster_open)
+      Ebml_EndSubElement(glob, &glob->startCluster);
+
+    /* Open the new cluster */
+    block_timecode = 0;
+    glob->cluster_open = 1;
+    glob->cluster_timecode = pts_ms;
+    glob->cluster_pos = ftello(glob->stream);
+    Ebml_StartSubElement(glob, &glob->startCluster, Cluster);  // cluster
+    Ebml_SerializeUnsigned(glob, Timecode, glob->cluster_timecode);
+
+    /* Save a cue point if this is a keyframe. */
+    if (is_keyframe) {
+      struct cue_entry *cue, *new_cue_list;
+
+      new_cue_list = reinterpret_cast<cue_entry*>(realloc(glob->cue_list,
+                     (glob->cues+1) * sizeof(struct cue_entry)));
+      if (new_cue_list) {
+        glob->cue_list = new_cue_list;
+      } else {
+        // TODO(frkoenig) : Handle this error better/correctly
+        fprintf(stderr, "\nFailed to realloc cue list.\n");
+        exit(EXIT_FAILURE);
+      }
+
+      cue = &glob->cue_list[glob->cues];
+      cue->time = glob->cluster_timecode;
+      cue->loc = glob->cluster_pos;
+      glob->cues++;
+    }
+  }
+
+  /* Write the Simple Block */
+  Ebml_WriteID(glob, SimpleBlock);
+
+  jlong frameSz =  env->GetLongField(jfd, lengthId);
+
+  block_length = frameSz + 4;
+  block_length |= 0x10000000;
+  Ebml_Serialize(glob, &block_length, sizeof(block_length), 4ULL);
+
+  track_number = 1;
+  track_number |= 0x80;
+  Ebml_Write(glob, &track_number, 1ULL);
+
+  Ebml_Serialize(glob, &block_timecode, sizeof(block_timecode), 2ULL);
+
+  flags = 0;
+  if (is_keyframe)
+    flags |= 0x80;
+  if (frameFlags & VPX_FRAME_IS_INVISIBLE)
+    flags |= 0x08;
+  Ebml_Write(glob, &flags, 1ULL);
+
+  jbyte *frameBuf = env->GetByteArrayElements((jbyteArray)jba, 0);
+
+  Ebml_Write(glob, frameBuf + offset, static_cast<uint64_t>(frameSz));
+
+  env->ReleaseByteArrayElements((jbyteArray)jba, frameBuf, 0);
+}
+
+FUNC(void, writeWebmFileFooter, jlong jglob, jlong hash) {
+  EbmlGlobal *glob = reinterpret_cast<EbmlGlobal*>(jglob);
+
+  //1
+  if (glob->cluster_open)
+    Ebml_EndSubElement(glob, &glob->startCluster);
+
+  {
+    EbmlLoc start;
+    unsigned int i;
+
+    glob->cue_pos = ftello(glob->stream);
+    Ebml_StartSubElement(glob, &start, Cues);
+  //2
+    for (i = 0; i < glob->cues; i++) {
+      struct cue_entry *cue = &glob->cue_list[i];
+      EbmlLoc start;
+
+      Ebml_StartSubElement(glob, &start, CuePoint);
+      {
+        EbmlLoc start;
+
+        Ebml_SerializeUnsigned(glob, CueTime, cue->time);
+
+        Ebml_StartSubElement(glob, &start, CueTrackPositions);
+        Ebml_SerializeUnsigned(glob, CueTrack, 1);
+        Ebml_SerializeUnsigned64(glob, CueClusterPosition,
+                                  cue->loc - glob->position_reference);
+        // Ebml_SerializeUnsigned(glob, CueBlockNumber, cue->blockNumber);
+        Ebml_EndSubElement(glob, &start);
+      }
+      Ebml_EndSubElement(glob, &start);
+    }
+    Ebml_EndSubElement(glob, &start);
+  }
+
+  Ebml_EndSubElement(glob, &glob->startSegment);
+
+  /* Patch up the seek info block */
+  write_webm_seek_info(glob);
+
+  /* Patch up the track id */
+  fseeko(glob->stream, glob->track_id_pos, SEEK_SET);
+  Ebml_SerializeUnsigned32(glob, TrackUID, hash);
+
+  fseeko(glob->stream, 0, SEEK_END);
+}
diff --git a/src/org/jitsi/impl/neomedia/recording/WebmWriter.java b/src/org/jitsi/impl/neomedia/recording/WebmWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3b930ada7b83ddc5dd908b8f8ff050889cdc4b4
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/recording/WebmWriter.java
@@ -0,0 +1,78 @@
+package org.jitsi.impl.neomedia.recording;
+
+import java.io.*;
+
+public class WebmWriter
+{
+    static
+    {
+        System.loadLibrary("jnvpx");
+    }
+
+    /**
+     * Constant corresponding to <tt>VPX_FRAME_IS_KEY</tt> from libvpx's
+     * <tt>vpx/vpx_encoder.h</tt>
+     */
+    public static int FLAG_FRAME_IS_KEY = 0x01;
+
+    /**
+     * Constant corresponding to <tt>VPX_FRAME_IS_INVISIBLE</tt> from libvpx's
+     * <tt>vpx/vpx_encoder.h</tt>
+     */
+    public static int FLAG_FRAME_IS_INVISIBLE = 0x04;
+
+    private long glob;
+
+    private native long allocCfg();
+
+    /**
+     * Free-s <tt>glob</tt> and closes the file opened for writing.
+     * @param glob
+     */
+    private native void freeCfg(long glob);
+
+    private native boolean openFile(long glob, String fileName);
+    private native void writeWebmFileHeader(long glob, int width, int height);
+    public void writeWebmFileHeader(int width, int height)
+    {
+        writeWebmFileHeader(glob, width, height);
+    }
+    private native void writeWebmBlock(long glob, FrameDescriptor fd);
+    private native void writeWebmFileFooter(long glob, long hash);
+
+    public WebmWriter(String filename)
+            throws IOException
+    {
+        glob = allocCfg();
+
+        if (glob == 0)
+        {
+            throw new IOException("allocCfg() failed");
+        }
+
+        if (openFile(glob, filename))
+        {
+            throw new IOException("Can not open " + filename + " for writing");
+        }
+    }
+
+    public void close()
+    {
+        writeWebmFileFooter(glob, 0);
+        freeCfg(glob); //also closes the file
+    }
+
+    public void writeFrame(FrameDescriptor fd)
+    {
+        writeWebmBlock(glob, fd);
+    }
+
+    public static class FrameDescriptor
+    {
+        public byte[] buffer;
+        public int offset;
+        public long length;
+        public long pts;
+        public int flags;
+    }
+}