2020年6月25日 星期四

FFMPEG: Decode and then encode frames to JPEG images

I've used FFMPEG library for a while. Actually, the FFMPEG library's decoding process flow can be described as the following picture. If you want to read the videos and then save to jpeg file, you can take a look on my programming code (tested on FFMPEG ver. 4 library).


// VideoProcessing.cpp 
//
#pragma once
using namespace std;
#include <iostream>
extern "C" 
{
    #include <libavcodec/avcodec.h>
    #include <libavdevice/avdevice.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavutil/avutil.h>
    #include <libswscale/swscale.h>
    #pragma comment(lib, "avcodec.lib")
    #pragma comment(lib, "avdevice.lib")
    #pragma comment(lib, "avformat.lib")
    #pragma comment(lib, "avfilter.lib")
    #pragma comment(lib, "avutil.lib")
    #pragma comment(lib, "swscale.lib")
}

void SaveToJPEG(AVFrame* pFrame, const char * folderName, int index)

{

    // Setup Output Path
    char outFile[256] = { 0 };
    sprintf_s(outFile, sizeof(outFile)/sizeof(outFile[0]), "%s\\OutputImages-%d.jpg", folderName, index);


    AVFormatContext* pFormatCtx = avformat_alloc_context();

    // Setup the output format
    pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);

    // Initializext 
    if (avio_open(&pFormatCtx->pb, outFile, AVIO_FLAG_READ_WRITE) < 0) {

        printf("Couldn't open output file.");

        return;

    }

    // Get a new Stream from the indicated format context
    AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);

    if (pAVStream == NULL) {

        return;

    }

    // Find encoder from the codec identifier.

    AVCodec* pCodec = avcodec_find_encoder(pFormatCtx->oformat->video_codec);
    
    if (!pCodec) {
        printf("Codec not found.");
        return;
    }
    // Setup the codec context
    AVCodecContext* codecCtx = avcodec_alloc_context3(pCodec);
    codecCtx->codec_id =  pFormatCtx->oformat->video_codec;
    codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
    codecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
    codecCtx->width = pFrame->width;
    codecCtx->height = pFrame->height;
    codecCtx->time_base = AVRational{ 1,25 };
    
    // Open the codec
    if (avcodec_open2(codecCtx, pCodec, NULL) < 0) {

        printf("Could not open codec.");

        return;

    }
    // assign the codec context to the stream parameters.
    avcodec_parameters_from_context(pAVStream->codecpar, codecCtx);


    //Write Header 
    avformat_write_header(pFormatCtx, NULL);

    int y_size = (codecCtx->width) * (codecCtx->height);

    // assign large enough space
    AVPacket pkt;

    av_new_packet(&pkt, y_size); 

    int got_picture = 0;

    // Use avcodec_send_frame()/avcodec_receive_packet() instead
    int ret = avcodec_send_frame(codecCtx, pFrame);

    if (ret < 0) {

        printf("Encode Error.\n");

        return;

    }
    else
    {
        ret = avcodec_receive_packet(codecCtx, &pkt);

        ret = av_write_frame(pFormatCtx, &pkt);

    }

    av_packet_unref(&pkt);

    //Write Trailer 
    av_write_trailer(pFormatCtx);

    printf("Encode Successful.\n");

    avcodec_close(codecCtx);

    avio_close(pFormatCtx->pb);
    
    avformat_free_context(pFormatCtx);
    avcodec_free_context(&codecCtx);
}

int main(int argc, char * argv[])
{

    if (argc < 2) {
        cout << "You need to specify a media file." << endl;
        cout << "Command line : VideoProcessing.exe [input_video_path] [output_folder]" << endl;
        return -1;
    }

    AVFormatContext* pFormatContext = avformat_alloc_context();
    if (!pFormatContext) {
        cout << "ERROR could not allocate memory for Format Context" << endl;
        return -1;
    }

    if (avformat_open_input(&pFormatContext, argv[1], NULL, NULL) != 0) {
        cout << "ERROR could not open the file" << endl;
        return -1;
    }

    if (avformat_find_stream_info(pFormatContext, NULL) < 0) {
        cout << "ERROR could not get the stream info" << endl;
        return -1;
    }

    // Initialize the codec, paramters for subsequent useage. 
    AVCodec* pCodec = NULL;
    AVCodecParameters* pCodecParameters = NULL;
    int videoStreamIndex = -1;

    for (int i = 0; i < pFormatContext->nb_streams; i++)
    {
        AVCodecParameters* pLocalCodecParameters = NULL;
        // Read the codec parameters corresponding to each stream.
        pLocalCodecParameters = pFormatContext->streams[i]->codecpar;

        AVCodec* pLocalCodec = NULL;
        pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id);

        if (pLocalCodec == NULL)
        {
            cout << "[ERROR] Cannot find the codec" << endl;
        }

        if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            if (videoStreamIndex == -1)
            {
                videoStreamIndex = i;
                pCodec = pLocalCodec;
                pCodecParameters = pLocalCodecParameters;
            }
        }
    }

    AVCodecContext* pCodecContext = avcodec_alloc_context3(pCodec);
    if (pCodecContext == NULL)
    {
        cout << "Fail to allocate the memoery to the Codec Context." << endl;
        return -1;
    }

    if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0)
    {
        cout << "failed to copy codec params to codec context" << endl;
        return -1;
    }

    if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
    {
        cout << "failed to open codec through avcodec_open2" << endl;
        return -1;
    }

    AVFrame* pFrame = av_frame_alloc();
    if (!pFrame)
    {
        cout << "failed to allocated memory for AVFrame" << endl;
        return -1;
    }

    AVPacket* pPacket = av_packet_alloc();
    if (!pPacket)
    {
        cout << "failed to allocated memory for AVPacket" << endl;
        return -1;
    }

    int indexOfFrame = 0;
    while (av_read_frame(pFormatContext, pPacket) >= 0)
    {
        // if it's the video stream
        if (pPacket->stream_index == videoStreamIndex) {
            int response = avcodec_send_packet(pCodecContext, pPacket);
            if (response < 0)
            {
                break;
            }
            else
            {
                response = avcodec_receive_frame(pCodecContext, pFrame);

                if (response >= 0)
                {
                    indexOfFrame++;
                    SaveToJPEG(pFrame, argv[2], indexOfFrame);
                }
            }
        }
        av_packet_unref(pPacket);
        // Limit the number of output frame to be 5.
        if (indexOfFrame == 5)
        {
            break;
        }

    }
    // https://ffmpeg.org/doxygen/trunk/group__lavc__packet.html#ga63d5a489b419bd5d45cfd09091cbcbc2
    avformat_close_input(&pFormatContext);
    av_frame_free(&pFrame);
    avcodec_free_context(&pCodecContext);
}
Reference:
  1. FFMPEG libav decode note
  2. FFMPEG libav tutorial
  3. 用AVCodecParameters代替AVCodecContext
  4. 用FFmpeg保存JPEG图片



2018年12月24日 星期一

Materials for Neural Network

Some materials are enclosed here.
1. 深度學習(二): 反向傳播 URL:http://chansh518.github.io/deep%20learning/2016/08/08/Deep-Learning-Notes-Backpropagation.html
2. 一文看懂常用的梯度下降算法 URL: https://blog.csdn.net/u013709270/article/details/78667531
3. 邏輯回歸代價函數及其梯度下降公式 URL: https://blog.csdn.net/Mr_HHH/article/details/78934793
4. The Back Propagation Algorithm. URL: https://page.mi.fu-berlin.de/rojas/neural/chapter/K7.pdf
5. Derivation of Back Propagation Algorithm for Forward Neural Networks. URL:  http://www.cs.put.poznan.pl/pliskowski/pub/teaching/eio/lab1/eio-supplementary.pdf
6. 凸優化 梯度下降。URL: http://www.hanlongfei.com/凸优化/2015/09/29/cmu-10725-gradient/
7. Geadient Descent demystified. URL:  https://towardsdatascience.com/gradient-descent-demystified-bc30b26e432a
8. An introduction to gradient descent and linear regression. URL: https://spin.atomicobject.com/2014/06/24/gradient-descent-linear-regression/

2018年5月1日 星期二

It's time to push my career forward

After a long time, almost 3 months, I've done my PhD degree. In the past, I could not image that I get my PhD. Thanks for my supervisor's and family's great supports. I did it!
Through the training of PhD, making things clear and be systematic are not the difficult parts anymore for me. But, I deeply felt that doing research tasks and engineering works was the most difficult part!

Now, I am a software engineer of an international corporate.
For me, doing both engineering and research work is better than doing only researches!
Now, it's time to push my career forward.

2017年8月19日 星期六

Build your service using Pritunl and Lets Encrypt

Pritunl is an open source enterprise VPN platform which is used to easily build free VPN service.
It is built on the top of TLS/SSL protocol and is not easily banned by some countries. :)
However, by the default settings of its tutorial, the generated TLS certificate for Pritunl is not recognized by browsers because its certificate is not authorized by third-party CA.

To mitigate the effect, Let's Encrypt, the well-known CA, is considered as the best choice for end-users.

This post briefs how to setup Let's Encrypt's SSL certificate and setup Pritunl on Ubuntu 14.04. You can follow the following easy steps to build your own VPN service.

  1. Go to Let's Encrypt and apply for a certificate by the certbot script.
  2. If you are using apache, nginx or Plesk, please stop and remove it first. Otherwise, the later Pritunl will have a conflict on port 80. 
  3. Follow the post of Vultr to setup Pritunl.
  4. Congratulations! You have your own VPN service.  


Trouble Shotting
If your Pritunl service cannot normally start, please check Pritunl's log in /var/log/pritunl.log. If the log displays the message like "127.0.0.1:27001 connection refused", please check MongoDB's log messages in /var/log/mongodb/mongod.log. You may find the following texts in the log file:
ERROR: Insufficient free space for journal files
Please make at least 3379MB available in /var/lib/mongo/journal or use --smallfiles

It seems that the disk space is not enough for running MongoDB and it will impact the service of Pritunl. To solve this issue, please add the following configuration in the configuration file (it is generally put in /etc/mongod.conf) of MongoDB.
  mmapv1:
    smallFiles: true

Then, restart MongoDB service by the command: service mongod restart

Finally, you may normally activate the Pritunl service.

2016年11月16日 星期三

Math Editor in C#

When you are acquiring to integrate a math editor into your application in WPF (Windows Presentation Foundation), I recommend you to use this math editor project called OOP in the Real World - Creating an Equation Editor in Code Project platform.

However, the source code put in Code Project is out of date. If you want a new version of the source code and start to program it, I give the instructions for you as follows:

  1. Download the Math_Editor_1.0.6.5.zip from http://www.codeproject.com/KB/architecture/522345/Math_Editor_1.0.6.5.zip  then get ICSharpCode.SharpZipLib.dll after extracting this zip file.
  2. Download the source codes from https://github.com/kashifimran/math-editor/releases. You can choose any relase version. In fact, I have tested the source codes with both versions 1.0.6.5 and 1.0.6.6 and they work.
  3. Extract the source codes and open the project solution.
  4. Put ICSharpCode.SharpZipLib.dll in your project if there’s no library refering to ICSharpCode.SharpZipLib.
  5. Right click on this project then click the item ‘project properties’. 
  6. Click the tab “Signing” then cancel the clickbox called “Sign the assembly”.

Afterall, you will build this project successfully.

2016年8月5日 星期五

Install CKAN 2.5 on Ubuntu 14.04 LTS

Author: Jyun-Yao Huang (Allen; allen501pc@gmail.com)
System Environment: Ubuntu Linux 14.04 LTS (64bits)

1. Update the metadata of packages

$ sudo apt-get update

2. Install apache2, nginx, apache’s module wsgi and the library of postgres- libpq5.

$ sudo apt-get install -y nginx apache2 libapache2-mod-wsgi libpq5

3. Download deb-package of python-ckan.
There are two ways to get CKAN deb package.
First, here you can download my provided deb package in the cloud storage. URL: https://drive.google.com/file/d/0B6PKt2-xNai_QlNfdEZfY05JRlE/view?usp=sharing
Or, download it from CKAN official site.

$ wget http://packaging.ckan.org/python-ckan_2.5-trusty_amd64.deb

4. Install the package.

$ sudo dpkg -i python-ckan_2.5-trusty_amd64.deb

5. Install postgresql.

$ sudo apt-get install postgresql

6. Check the encoding format of Postgresql is UTF-8.

$ sudo -u postgres psql -l

7. Create the database account and the corresponding database. In this case, the account is ckan_default and the database is also called ckan_default.
Please remember your password of your database account.

$ sudo -u postgres createuser -S -D -R -P ckan_default
$ sudo -u postgres createdb -O ckan_default ckan_default -E utf-8

8. Edit the file at /etc/ckan/default/production.ini with super user’s permission. Check the settings below:

sqlalchemy.url = postgresql://ckan_default:pass@localhost/ckan_default

Please replace words in bold with the password of your created account for Postgresql.

9. Create the fold for CKAN storage. Type the following command to create storage.

$ sudo mkdir /var/lib/ckan
$ chown –R www-data:www-data /var/lib/ckan

10. Edit the file at /etc/ckan/default/production.ini with super user’s permission. Check the settings below:

ckan.storage_path = /var/lib/ckan

11. Download Solr 5.4.1
Warning: Form the CKAN official guide, if you install solr-jetty in Ubuntu 14.04, the JSP of Solr will not be supported. We have to install Solr.
There are two ways to get Solr package.
First, here you can download my provided Solr package in the cloud storage. URL: https://drive.google.com/open?id=0B6PKt2-xNai_Tm9sSnJoZmx1VW8
Or download it from Solr's site.

$ wget http://apache.mirror1.spango.com/lucene/solr/5.4.1/solr-5.4.1.tg

12. Install Solr 5.4.1

$ tar xzf solr-5.4.1.tgz solr-5.4.1/bin/install_solr_service.sh --strip-components=2
$ sudo chmod +x install_solr_service.sh
$ sudo ./install_solr_service.sh solr-5.4.1.tgz -f

13. Prepare the CKAN’s schema for Solr
In this case, we prepare a core named ckan for Solr.

$ sudo su
root$ mkdir /var/solr/data/ckan
root$ touch /var/solr/data/ckan/core.properties
root$ cp -a /opt/solr/server/solr/configsets/basic_configs/conf /var/solr/data/ckan
root$ mv /var/solr/data/ckan/conf/schema.xml /var/solr/data/ckan1/conf/schema.xml.orig
root$ ln -s /usr/lib/ckan/default/src/ckan/ckan/config/solr/schema.xml /var/solr/data/ckan/conf/schema.xml
root$ chown -R solr:solr /var/solr
root$ exit

14. [Support of Chinese Segmentation] Download the libraries for supporting Chinese segmentation in Solr. There are two packages: mmseg4j-core-x.y.z.jar and mmseg4j-lang-x.y.z.jar to be downloaded. I’ve prepared the zipped file – mmseg4j.zip at https://drive.google.com/open?id=0B6PKt2-xNai_a1luR20wQ0JtenM.

15. [Support of Chinese Segmentation] After downloading mmseg4j.zip and unzipping it into mmseg4j-core-1.10.0.jar and mmseg4j-solr-2.3.0.jar, upload JARs under the folder - /opt/solr/server/solr-webapp/webapp/WEB-INF/lib.

16. [Support of Chinese Segmentation]Modify the schema file at /usr/lib/ckan/default/src/ckan/ckan/config/solr/schema.xml to let Solr support Chinese segmentation.

<fieldType name="text" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
<filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.ASCIIFoldingFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
<filter class="solr.SnowballPorterFilterFactory" language="English" protected="protwords.txt"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.ASCIIFoldingFilterFactory"/>
</analyzer>
</fieldType>

17. Restart Solr.

$ sudo service solr restart

18. Serve the following URL to create the core called ckan via your browser.
http://127.0.0.1:8983/solr/admin/cores?action=CREATE&name=ckan&configSet=ckan

19. Edit the file at /etc/ckan/default/production.ini with super user’s permission. Check the setting for Solr below:

solr_url = http://127.0.0.1:8983/solr/ckan

In this case, ckan is the core we used in Solr, so we add ckan as suffix of the URL.

20. Initialize your database. We have to use VirtualEnv which is a virtual environment written in Python. Here, (virtualEnv) is the mark for denoting you are in VirtualEnv.

$ . /usr/lib/ckan/default/bin/activate
$ (virtualEnv) cd /usr/lib/ckan/default/src/ckan
$ (virtualEnv) paster db init -c /etc/ckan/default/production.ini
$ (virtualEnv) deactivate

21. Create Administrator for CKAN.

$ . /usr/lib/ckan/default/bin/activate
$ (virtualEnv) cd /usr/lib/ckan/default/src/ckan
$ (virtualEnv) paster sysadmin add admin -c /etc/ckan/default/production.ini
$ (virtualEnv) deactivate

22. Restart apache2 and nginx.

$ sudo service apache2 restart
$ sudo service nginx restart

After that, you will get the following CKAN site.

Image 39

Support of SSL

With the support of SSL, we can enhance the security of CKAN.

First, we have to apply for an SSL certificate. For freelancers, we can request the SSL certificate from Let’s Encrypt. The method of applying such certificate has been introduced in the official site of Let’s Encrypt. Interested readers can refers to its documents.

After getting an SSL certificate, please following the instructions in the post: Setting up CKAN with SSL.

REFERENCE

  1. CKAN, “Installing CKAN from Package,” CKAN. [Online]. Available: http://docs.ckan.org/en/latest/maintaining/installing/install-from-package.html
  2. CKAN, “Installing CKAN from Source,” CKAN. [Online]. Available: http://docs.ckan.org/en/latest/maintaining/installing/install-from-source.html
  3. Koen Vlasinkel, “How to Install Solr 5.2.1 on Ubuntu 14.04,” Digital Ocean Community. [Online]. Available: https://www.digitalocean.com/community/tutorials/how-to-install-solr-5-2-1-on-ubuntu-14-04
  4. Hvwaldow, “How to Upgrade to SOLR 5.3.1 on Debian Jessie for Use with CKAN,” CKAN. [Online]. Available: https://gist.github.com/hvwaldow/67fecf80a9790b5c9153#filesolr-upgrade-md
  5. CKAN2 安裝教學. URL: http://ckan-docs-tw.readthedocs.io/zh_TW/2.4/install.html
  6. CKAN, “Setting up CKAN with SSL,”. [Online]. Available: https://github.com/ckan/ckan/wiki/SSL