Thursday, July 25, 2013

MPEG DASH Adaptive Streaming

MPEG DASH Adaptive Streaming

Specifications

MPEG DASH is MPEG's standardized Adaptive Streaming over HTTP. It is specified in the following international standards:

ISO/IEC 23009-1 specifies the overall DASH architecture and the XML syntax for the MPD (Media Presentation Document)
ISO/IEC 23001-7 specifies the Common Encryption for MP4 fragments
ISO/IEC 14496-12/AMD 3 specifies the extensions to 14496-12 that are necessary to support DASH with fragmented MP4 media
Fragmenting MP4 files

To create DASH MP4 content, you need to start with fragmented MP4 files.
The command line tool mp4info can tell you if an MP4 file is fragmented or not: here's an example of what you will see in the 'Movie:' part of the mp4info output for a non-fragmented MP4 file (the line "fragments: no")

...
Movie:
  duration:   147188 ms
  time scale: 2997
  fragments:  no

Found 2 Tracks
...
If you have non-fragmented MP4 files, you can use mp4fragment to fragment them.

Example

mp4fragment somefile.mp4 somefile-fragmented.mp4

Generating the MPD

Once you have fragmented MP4 files, you need to create an MPD. For single-bitrate streaming, a single MP4 file is required. For multi-bitrate streaming, you will need a set of MP4 files that have been encoded with closed GOPs (Group Of Pictures) with equal durations. Also, the audio track in all the files should be encoded with the same parameters. Once you have your input files, you can use the python script mp4-dash.py (located under Source/Python/utils) to automatically generate the DASH MPD and optionally split the MP4 file into individual file segments. This script needs to invoke the Bento4 command line binaries mp4dump, mp4encrypt, mp4info, and mp4split compiled from the source, so you'll need to use the '--exec-dir' option to specify the directory where your built binaries reside (platform dependent).

If you just run the 'mp4-dash.py' script by itself, it will print out the options that are supported:

Usage: mp4-dash.py [options] <media-file> [<media-file> ...]

Each <media-file> is the path to a fragmented MP4 file, optionally prefixed
with a stream selector delimited by [ and ]. The same input MP4 file may be
repeated, provided that the stream selector prefixes select different streams.
Version 1.4.0 r557

Options:
  -h, --help            show this help message and exit
  --verbose             Be verbose
  --debug               Print out debugging information
  -o <output-dir>, --output-dir=<output-dir>
                        Output directory
  -f, --force           Allow output to an existing directory
  --init-segment=<filename>
                        Initialization segment name
  -m <filename>, --mpd-name=<filename>
                        MPD file name
  --no-media            Do not output media files (MPD/Manifests only)
  --rename-media        Use a file name pattern instead of the base name of
                        input files for output media files.
  --no-split            Do not split the file into individual segment files
  --use-segment-list    Use segment lists instead of segment templates
  --use-segment-template-number-padding
                        Use padded numbers in segment URL/filename templates
  --use-segment-timeline
                        Use segment timelines (necessary if segment durations
                        vary)
  --min-buffer-time=<duration>
                        Minimum buffer time (in seconds)
  --max-playout-rate=<strategy>
                        Max Playout Rate setting strategy for trick-play
                        support. Supported strategies: lowest:X
  --language-map=<lang_from>:<lang_to>[,...]
                        Remap language code <lang_from> to <lang_to>. Multiple
                        mappings can be specified, separated by ','
  --smooth              Produce an output compatible with Smooth Streaming
  --smooth-client-manifest-name=<filename>
                        Smooth Streaming Client Manifest file name
  --smooth-server-manifest-name=<filename>
                        Smooth Streaming Server Manifest file name
  --hippo               Produce an output compatible with the Hippo Media
                        Server
  --hippo-server-manifest-name=<filename>
                        Hippo Media Server Manifest file name
  --encryption-key=<KID>:<key>
                        Encrypt all audio and video tracks with AES key <key>
                        (in hex) and KID <KID> (in hex). Alternatively, the
                        <key> can be specified as the character '#' followed
                        by a base64-encoded key seed.
  --encryption-args=<cmdline-arguments>
                        Pass additional command line arguments to mp4encrypt
                        (separated by spaces)
  --use-compat-namespace
                        Use the original DASH MPD namespace as it was
                        specified in the first published specification
  --marlin              Add Marlin signaling to the MPD (requires an encrypted
                        input, or the --encryption-key option)
  --playready           Add PlayReady signaling to the MPD (requires an
                        encrypted input, or the --encryption-key option)
  --playready-header=<playready-header>
                        Add a PlayReady PRO element in the MPD. The use of
                        this option implied the --playready option.The
                        <playready-header> argument can be either: (1) the
                        name of a file containing a PlayReady XML Rights
                        Management Header (<WRMHEADER>) or a PlayReady Header
                        Object (PRO) in binary form,  or (2) the character '#'
                        followed by a PlayReady Header Object encoded in
                        Base64, or (3) one or more <name>:<value> pair(s)
                        (separated by '#' if more than one) specifying fields
                        of a PlayReady Header Object (field names include
                        LA_URL, LUI_URL and DS_ID)
  --playready-add-pssh  Store the PlayReady header in a 'pssh' box in the init
                        segment(s)
  --exec-dir=<exec_dir>
                        Directory where the Bento4 executables are located

Saturday, June 29, 2013

AbstractMPDElement &



/*


* AbstractMPDElement.cpp


*****************************************************************************


* Copyright (C) 2013, bitmovin Softwareentwicklung OG, All Rights Reserved


*

*


* This source code and its use and distribution, is subject to the terms


* and conditions of the applicable license agreement.


*****************************************************************************/






#include "AbstractMPDElement.h"






using namespace dash::mpd;


using namespace dash::xml;






AbstractMPDElement::AbstractMPDElement ()


{


}


AbstractMPDElement::~AbstractMPDElement ()


{


for(size_t i = 0; i < this->additionalSubNodes.size(); i++)


delete(this->additionalSubNodes.at(i));


}






const std::vector<INode *> AbstractMPDElement::GetAdditionalSubNodes () const


{


return this->additionalSubNodes;


}


const std::map<std::string, std::string> AbstractMPDElement::GetRawAttributes () const


{


return this->rawAttributes;


}


void AbstractMPDElement::AddAdditionalSubNode (INode *node)


{


this->additionalSubNodes.push_back(node);


}


void AbstractMPDElement::AddRawAttributes (std::map<std::string, std::string> attributes)


{


this->rawAttributes = attributes;


}


//SegmentURL method






namespace dash




{


namespace mpd


{


class SegmentURL : public ISegmentURL, public AbstractMPDElement


{


public:


SegmentURL ();


virtual ~SegmentURL();






const std::string& GetMediaURI () const;


const std::string& GetMediaRange () const;


const std::string& GetIndexURI () const;


const std::string& GetIndexRange () const;


ISegment* ToMediaSegment (const std::vector<IBaseUrl *>& baseurls) const;


ISegment* ToIndexSegment (const std::vector<IBaseUrl *>& baseurls) const;






void SetMediaURI (const std::string& mediaURI);


void SetMediaRange (const std::string& mediaRange);


void SetIndexURI (const std::string& indexURI);


void SetIndexRange (const std::string& indexRange);






private:


std::string mediaURI;


std::string mediaRange;


std::string indexURI;


std::string indexRange;


};


}


}

Saturday, June 22, 2013

Calculates motion vector using ffmpeg and libavcodec

//MOTION_VECTOR: Calculates motion vector using ffmpeg and libavcodec.

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define IS_INTERLACED(a) ((a)&MB_TYPE_INTERLACED)
#define IS_16X16(a)      ((a)&MB_TYPE_16x16)
#define IS_16X8(a)       ((a)&MB_TYPE_16x8)
#define IS_8X16(a)       ((a)&MB_TYPE_8x16)
#define IS_8X8(a)        ((a)&MB_TYPE_8x8)
#define USES_LIST(a, list) ((a) & ((MB_TYPE_P0L0|MB_TYPE_P1L0)<<(2*(list))))

int count = 0;
double sum = 0;
double h;
double w;

bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx, 
    int videoStream, AVFrame *pFrame)
{
    static AVPacket packet;
    static int      bytesRemaining = 0;
    static uint8_t  *rawData;
    static bool     fFirstTime = true;
    int             bytesDecoded;
    int             frameFinished;

    // First time we're called, set packet.data to NULL to indicate it
    // doesn't have to be freed
    if (fFirstTime) {
        fFirstTime = false;
        packet.data = NULL;
    }

    // Decode packets until we have decoded a complete frame
    while (true) {
        // Work on the current packet until we have decoded all of it
        while (bytesRemaining > 0) {
            // Decode the next chunk of data
            bytesDecoded = avcodec_decode_video(pCodecCtx, pFrame,
                &frameFinished, rawData, bytesRemaining);

            // Was there an error?
            if (bytesDecoded < 0) {
                fprintf(stderr, "Error while decoding frame\n");
                return false;
            }

            bytesRemaining -= bytesDecoded;
            rawData += bytesDecoded;

            // Did we finish the current frame? Then we can return
            if (frameFinished)
                return true;
        }

        // Read the next packet, skipping all packets that aren't for this
        // stream
        do {
            // Free old packet
            if (packet.data != NULL)
                av_free_packet(&packet);

            // Read new packet
            if (av_read_packet(pFormatCtx, &packet) < 0)
                goto loop_exit;
        } while (packet.stream_index != videoStream);

        bytesRemaining = packet.size;
        rawData = packet.data;
    }

loop_exit:

    // Decode the rest of the last frame
    bytesDecoded = avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
        rawData, bytesRemaining);

    // Free last packet
    if (packet.data != NULL)
        av_free_packet(&packet);

    fprintf(stderr, "finished: %d\n", frameFinished);
    return frameFinished != 0;
}

void print_vector(int x, int y, int dx, int dy)
{
 if (dx != 10000 && dy != 10000){
  sum = sum + sqrt((double)dx*(double)dx/w/w + (double)dy*(double)dy/h/h);
  count++;
 }
    //printf("%d %d ; %d %d\n", x, y, dx, dy);
}

/* Print motion vector for each macroblock in this frame.  If there is
 * no motion vector in some macroblock, it prints a magic number NO_MV. */
void printMVMatrix(int index, AVFrame *pict, AVCodecContext *ctx)
{
    const int mb_width  = (ctx->width + 15) / 16;
    const int mb_height = (ctx->height + 15) / 16;
    const int mb_stride = mb_width + 1;
    const int mv_sample_log2 = 4 - pict->motion_subsample_log2;
    const int mv_stride = (mb_width << mv_sample_log2) + (ctx->codec_id == CODEC_ID_H264 ? 0 : 1);
    const int quarter_sample = (ctx->flags & CODEC_FLAG_QPEL) != 0;
    const int shift = 1 + quarter_sample;


    //printf("frame %d, %d x %d\n", index, mb_height, mb_width);

    for (int mb_y = 0; mb_y < mb_height; mb_y++) {
 for (int mb_x = 0; mb_x < mb_width; mb_x++) {
     const int mb_index = mb_x + mb_y * mb_stride;
     if (pict->motion_val) {
  for (int type = 0; type < 3; type++) {
      int direction = 0;
      switch (type) {
   case 0:
       if (pict->pict_type != FF_P_TYPE)
    continue;
       direction = 0;
       break;
   case 1:
       if (pict->pict_type != FF_B_TYPE)
           continue;
       direction = 0;
       break;
   case 2:
       if (pict->pict_type != FF_B_TYPE)
    continue;
       direction = 1;
       break;
      }

      if (!USES_LIST(pict->mb_type[mb_index], direction)) {
#define NO_MV 10000
   if (IS_8X8(pict->mb_type[mb_index])) {
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
   } else if (IS_16X8(pict->mb_type[mb_index])) {
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
   } else if (IS_8X16(pict->mb_type[mb_index])) {
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
   } else {
       print_vector(mb_x, mb_y, NO_MV, NO_MV);
   }
#undef NO_MV
   continue;
      }

      if (IS_8X8(pict->mb_type[mb_index])) {
   for (int i = 0; i < 4; i++) {
       int xy = (mb_x*2 + (i&1) + (mb_y*2 + (i>>1))*mv_stride) << (mv_sample_log2-1);
       int dx = (pict->motion_val[direction][xy][0]>>shift);
       int dy = (pict->motion_val[direction][xy][1]>>shift);
       print_vector(mb_x, mb_y, dx, dy);
   }
      } else if (IS_16X8(pict->mb_type[mb_index])) {
   for (int i = 0; i < 2; i++) {
       int xy = (mb_x*2 + (mb_y*2 + i)*mv_stride) << (mv_sample_log2-1);
       int dx = (pict->motion_val[direction][xy][0]>>shift);
       int dy = (pict->motion_val[direction][xy][1]>>shift);

       if (IS_INTERLACED(pict->mb_type[mb_index]))
    dy *= 2;

       print_vector(mb_x, mb_y, dx, dy);
   }
      } else if (IS_8X16(pict->mb_type[mb_index])) {
   for (int i = 0; i < 2; i++) {
       int xy =  (mb_x*2 + i + mb_y*2*mv_stride) << (mv_sample_log2-1);
       int dx = (pict->motion_val[direction][xy][0]>>shift);
       int dy = (pict->motion_val[direction][xy][1]>>shift);

       if (IS_INTERLACED(pict->mb_type[mb_index]))
    dy *= 2;

       print_vector(mb_x, mb_y, dx, dy);
   }
      } else {
   int xy = (mb_x + mb_y*mv_stride) << mv_sample_log2;
   int dx = (pict->motion_val[direction][xy][0]>>shift);
   int dy = (pict->motion_val[direction][xy][1]>>shift);
   print_vector(mb_x, mb_y, dx, dy);
      }
  }
     }
     //printf("--\n");
 }
 //printf("====\n");
    }
}

int main(int argc, char *argv[])
{
    AVFormatContext *pFormatCtx;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    AVFrame         *pFrame; 
    int             videoStream;

    //save height and width
    h = strtod(argv[2],NULL);
    w = strtod(argv[3],NULL);

    // Register all formats and codecs
    av_register_all();

    // Open video file
    if (av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL) != 0)
        return -1; // Couldn't open file

    // Retrieve stream information
    if (av_find_stream_info(pFormatCtx) < 0)
        return -1; // Couldn't find stream information

    // Dump information about file onto standard error
//    dump_format(pFormatCtx, 0, argv[1], false);

    // Find the first video stream
    videoStream = -1;
    for (int i = 0; i < pFormatCtx->nb_streams; i++) {
 AVCodecContext *cc = pFormatCtx->streams[i]->codec;
        if (cc->codec_type==CODEC_TYPE_VIDEO) {
     // don't care FF_DEBUG_VIS_MV_B_BACK
     cc->debug_mv = FF_DEBUG_VIS_MV_P_FOR | FF_DEBUG_VIS_MV_B_FOR;
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1)
        return -1; // Didn't find a video stream

    // Get a pointer to the codec context for the video stream
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    // Find the decoder for the video stream
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL)
        return -1; // Codec not found

    // Inform the codec that we can handle truncated bitstreams -- i.e.,
    // bitstreams where frame boundaries can fall in the middle of packets
    if (pCodec->capabilities & CODEC_CAP_TRUNCATED)
        pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;

    // Open codec
    if (avcodec_open(pCodecCtx, pCodec)<0)
        return -1; // Could not open codec

    // Hack to correct wrong frame rates that seem to be generated by some 
    // codecs
//    if (pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)
//        pCodecCtx->frame_rate_base=1000;

    // Allocate video frame
    pFrame = avcodec_alloc_frame();


    int f = 1;

    while  (GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame)) {

 // Ignore I-Frame
 if (pFrame->pict_type != FF_I_TYPE)
     printMVMatrix(f, pFrame, pCodecCtx);

 ++f;
    }

    av_free(pFrame);
    avcodec_close(pCodecCtx);
    av_close_input_file(pFormatCtx);

    //printf("sum mv: %f, total # mv: %d\n",sum,count);
    printf("%f",sum/(double)count);

    return 0;
}

Tuesday, June 11, 2013

Hash Map (Hash Table) implementation in C++



template <typename K, typename V, typename F = KeyHash<K>>

class HashMap {
public:
HashMap() {
// construct zero initialized hash table of size
table = new HashNode<K, V> *[TABLE_SIZE]();
}

~HashMap() {

// destroy all buckets one by one

for (int i = 0; i < TABLE_SIZE; ++i) {

HashNode<K, V> *entry = table[i];

while (entry != NULL) {

HashNode<K, V> *prev = entry;
entry = entry->getNext();
delete prev;
}
table[i] = NULL;
}
// destroy the hash table
delete [] table;
}

bool get(const K &key, V &value) {
unsigned long hashValue = hashFunc(key);
HashNode<K, V> *entry = table[hashValue];
while (entry != NULL) {
if (entry->getKey() == key) {
value = entry->getValue();
return true;
}
entry = entry->getNext();
}
return false;
}

void put(const K &key, const V &value) {
unsigned long hashValue = hashFunc(key);
HashNode<K, V> *prev = NULL;
HashNode<K, V> *entry = table[hashValue];
while (entry != NULL && entry->getKey() != key) {
prev = entry;
entry = entry->getNext();
}
if (entry == NULL) {
entry = new HashNode<K, V>(key, value);
if (prev == NULL) {
// insert as first bucket
table[hashValue] = entry;
} else {
prev->setNext(entry);
}
} else {
// just update the value
entry->setValue(value);
}
}

void remove(const K &key) {
unsigned long hashValue = hashFunc(key);
HashNode<K, V> *prev = NULL;
HashNode<K, V> *entry = table[hashValue];
while (entry != NULL && entry->getKey() != key) {
prev = entry;
entry = entry->getNext();
}

if (entry == NULL) {
// key not found

return;
}
else {
if (prev == NULL) {
// remove first bucket of the list
table[hashValue] = entry->getNext();

} else {
prev->setNext(entry->getNext());
}
delete entry;
}
}

private:

// hash table
HashNode<K, V> **table;
F hashFunc;
};