Recherche avancée

Médias (91)

Autres articles (61)

  • Ecrire une actualité

    21 juin 2013, par

    Présentez les changements dans votre MédiaSPIP ou les actualités de vos projets sur votre MédiaSPIP grâce à la rubrique actualités.
    Dans le thème par défaut spipeo de MédiaSPIP, les actualités sont affichées en bas de la page principale sous les éditoriaux.
    Vous pouvez personnaliser le formulaire de création d’une actualité.
    Formulaire de création d’une actualité Dans le cas d’un document de type actualité, les champs proposés par défaut sont : Date de publication ( personnaliser la date de publication ) (...)

  • MediaSPIP 0.1 Beta version

    25 avril 2011, par

    MediaSPIP 0.1 beta is the first version of MediaSPIP proclaimed as "usable".
    The zip file provided here only contains the sources of MediaSPIP in its standalone version.
    To get a working installation, you must manually install all-software dependencies on the server.
    If you want to use this archive for an installation in "farm mode", you will also need to proceed to other manual (...)

  • Submit bugs and patches

    13 avril 2011

    Unfortunately a software is never perfect.
    If you think you have found a bug, report it using our ticket system. Please to help us to fix it by providing the following information : the browser you are using, including the exact version as precise an explanation as possible of the problem if possible, the steps taken resulting in the problem a link to the site / page in question
    If you think you have solved the bug, fill in a ticket and attach to it a corrective patch.
    You may also (...)

Sur d’autres sites (3453)

  • Cohort Analysis 101 : How-To, Examples & Top Tools

    13 novembre 2023, par Erin — Analytics Tips

    Imagine that a farmer is trying to figure out why certain hens are laying large brown eggs and others are laying average-sized white eggs.

    The farmer decides to group the hens into cohorts based on what kind of eggs they lay to make it easier to detect patterns in their day-to-day lives. After careful observation and analysis, she discovered that the hens laying big brown eggs ate more than the roost’s other hens.

    With this cohort analysis, the farmer deduced that a hen’s body weight directly corresponds to egg size. She can now develop a strategy to increase the body weight of her hens to sell more large brown eggs, which are very popular at the weekly farmers’ market.

    Cohort analysis has a myriad of applications in the world of web analytics. Like our farmer, you can use it to better understand user behaviour and reap the benefits of your efforts. This article will discuss the best practices for conducting an effective cohort analysis and compare the top cohort analysis tools for 2024. 

    What is cohort analysis ?

    By definition, cohort analysis refers to a technique where users are grouped based on shared characteristics or behaviours and then examined over a specified period.

    Think of it as a marketing superpower, enabling you to comprehend user behaviours, craft personalised campaigns and allocate resources wisely, ultimately resulting in improved performance and better ROI.

    Why does cohort analysis matter ?

    In web analytics, a cohort is a group of users who share a certain behaviour or characteristic. The goal of cohort analysis is to uncover patterns and compare the performance and behaviour of different cohorts over time.

    An example of a cohort is a group of users who made their first purchase during the holidays. By analysing this cohort, you could learn more about their behaviour and buying patterns. You may discover that this cohort is more likely to buy specific product categories as holiday gifts — you can then tailor future holiday marketing campaigns to include these categories. 

    Types of cohort analysis

    There are a few different types of notable cohorts : 

    1. Time-based cohorts are groups of users categorised by a specific time. The example of the farmer we went over at the beginning of this section is a great example of a time-based cohort.
    2. Acquisition cohorts are users acquired during a specific time frame, event or marketing channel. Analysing these cohorts can help you determine the value of different acquisition methods. 
    3. Behavioural cohorts consist of users who show similar patterns of behaviour. Examples include frequent purchases with your mobile app or digital content engagement. 
    4. Demographic cohorts share common demographic characteristics like age, gender, education level and income. 
    5. Churn cohorts are buyers who have cancelled a subscription/stopped using your service within a specific time frame. Analysing churn cohorts can help you understand why customers leave.
    6. Geographic cohorts are pretty self-explanatory — you can use them to tailor your marketing efforts to specific regions. 
    7. Customer journey cohorts are based on the buyer lifecycle — from acquisition to adoption to retention. 
    8. Product usage cohorts are buyers who use your product/service specifically (think basic users, power users or occasional users). 

    Best practices for conducting a cohort analysis 

    So, you’ve decided you want to understand your user base better but don’t know how to go about it. Perhaps you want to reduce churn and create a more engaging user experience. In this section, we’ll walk you through the dos and don’ts of conducting an effective cohort analysis. Remember that you should tailor your cohort analysis strategy for organisation-specific goals.

    A line graph depicting product usage cohort data with a blue line for new users and a green line for power users.

    1. Preparing for cohort analysis : 

      • First, define specific goals you want your cohort analysis to achieve. Examples include improving conversion rates or reducing churn.
      • Choosing the right time frame will help you compare short-term vs. long-term data trends. 

    2. Creating effective cohorts : 

      • Define your segmentation criteria — anything from demographics to location, purchase history or user engagement level. Narrowing in on your specific segments will make your cohort analysis more precise. 
      • It’s important to find a balance between cohort size and similarity. If your cohort is too small and diverse, you won’t be able to find specific behavioural patterns.

    3. Performing cohort analysis :

        • Study retention rates across cohorts to identify patterns in user behaviour and engagement over time. Pay special attention to cohorts with high retention or churn rates. 
        • Analysing cohorts can reveal interesting behavioural insights — how do specific cohorts interact with your website ? Do they have certain preferences ? Why ? 

    4. Visualising and interpreting data :

      • Visualising your findings can be a great way to reveal patterns. Line charts can help you spot trends, while bar charts can help you compare cohorts.
      • Guide your analytics team on how to interpret patterns in cohort data. Watch for sudden drops or spikes and what they could mean. 

    5. Continue improving :

      • User behaviour is constantly evolving, so be adaptable. Continuous tracking of user behaviour will help keep your strategies up to date. 
      • Encourage iterative analysis optimisation based on your findings. 
    wrench trying to hammer in a nail, and a hammer trying to screw in a screw to a piece of wood

    The top cohort analysis tools for 2024

    In this section, we’ll go over the best cohort analysis tools for 2024, including their key features, cohort analysis dashboards, cost and pros and cons.

    1. Matomo

    A screenshot of a cohorts graph in Matomo

    Matomo is an open-source, GDPR-compliant web analytics solution that offers cohort analysis as a standard feature in Matomo Cloud and is available as a plugin for Matomo On-Premise. Pairing traditional web analytics with cohort analysis will help you gain even deeper insights into understanding user behaviour over time. 

    You can use the data you get from web analytics to identify patterns in user behaviour and target your marketing strategies to specific cohorts. 

    Key features

    • Matomo offers a cohorts table that lets you compare cohorts side-by-side, and it comes with a time series.
      • All core session and conversion metrics are also available in the Cohorts report.
    • Create custom segments based on demographics, geography, referral sources, acquisition date, device types or user behaviour. 
    • Matomo provides retention analysis so you can track how many users from a specific cohort return to your website and when. 
    • Flexibly analyse your cohorts with custom reports. Customise your reports by combining metrics and dimensions specific to different cohorts. 
    • Create cohorts based on events or interactions with your website. 
    • Intuitive, colour-coded data visualisation, so you can easily spot patterns.

    Pros

    • No setup is needed if you use the JavaScript tracker
    • You can fetch cohort without any limit
    • 100% accurate data, no AI or Machine Learning data filling, and without the use of data sampling

    Cons

    • Matomo On-Premise (self-hosted) is free, but advanced features come with additional charges
    • Servers and technical know-how are required for Matomo On-Premise. Alternatively, for those not ready for self-hosting, Matomo Cloud presents a more accessible option and starts at $19 per month.

    Price : 

    • Matomo Cloud : 21-day free trial, then starts at $19 per month (includes Cohorts).
    • Matomo On-Premise : Free to self-host ; Cohorts plugin : 30-day free trial, then $99 per year.

    2. Mixpanel

    Mixpanel is a product analytics tool designed to help teams better understand user behaviour. It is especially well-suited for analysing user behaviour on iOS and Android apps. It offers various cohort analytics features that can be used to identify patterns and engage your users. 

    Key features

    • Create cohorts based on criteria such as sign-up date, first purchase date, referral source, geographic location, device type or another custom event/property. 
    • Compare how different cohorts engage with your app with Mixpanel’s comparative analysis features.
    • Create interactive dashboards, charts and graphs to visualise data.
    • Mixpanel provides retention analysis tools to see how often users return to your product over time. 
    • Send targeted messages and notifications to specific cohorts to encourage user engagement, announce new features, etc. 
    • Track and analyse user behaviours within cohorts — understand how different types of users engage with your product.

    Pros

    • Easily export cohort analysis data for further analysis
    • Combined with Mixpanel reports, cohorts can be a powerful tool for improving your product

    Cons

    • With the free Mixpanel plan, you can’t save cohorts for future use
    • Enterprise-level pricing is expensive
    • Time-consuming cohort creation process

    Price : Free basic version. The growth version starts at £16/month.

    3. Amplitude

    A screenshot of a cohorts graph in Amplitude

    Amplitude is another product analytics solution that can help businesses track user interactions across digital platforms. Amplitude offers a standard toolkit for in-depth cohort analysis.

    Key features

    • Create cohorts based on criteria such as sign-up date, first purchase date, referral source, geographic location, device type or another custom event/property. 
    • Conduct behavioural, time-based and retention analyses.
    • Create custom reports with custom data.
    • Segment cohorts further based on additional criteria and compare multiple cohorts side-by-side.

    Pros

    • Highly customisable and flexible
    • Quick and simple setup

    Cons

    • Steep learning curve — requires significant training 
    • Slow loading speed
    • High price point compared to other tools

    Price : Free basic version. Plus version starts at £40/month (billed annually).

    4. Kissmetrics

    A screenshot of a cohorts graph in Kissmetrics

    Kissmetrics is a customer engagement automation platform that offers powerful analytics features. Kissmetrics provides behavioural analytics, segmentation and email campaign automation. 

    Key features

    • Create cohorts based on demographics, user behaviour, referral sources, events and specific time frames.
    • The user path tool provides path visualisation so you can identify common paths users take and spot abandonment points. 
    • Create and optimise conversion funnels.
    • Customise events, user properties, funnels, segments, cohorts and more.

    Pros

    • Powerful data visualisation options
    • Highly customisable

    Cons

    • Difficult to install
    • Not well-suited for small businesses
    • Limited integration with other tools

    Price : Starting at £21/month for 10k events (billed monthly).

    Improve your cohort analysis with Matomo

    When choosing a cohort analysis tool, consider factors such as the tool’s ease of integration with your existing systems, data accuracy, the flexibility it offers in defining cohorts, the comprehensiveness of reporting features, and its scalability to accommodate the growth of your data and analysis needs over time. Moreover, it’s essential to confirm GDPR compliance to uphold rigorous privacy standards. 

    If you’re ready to understand your user’s behaviour, take Matomo for a test drive. Paired with web analytics, this powerful combination can advance your marketing efforts. Start your 21-day free trial today — no credit card required.

  • Getting and decoding video by RTP H264

    29 novembre 2023, par AlekseiKraev

    Parsing RTP H264.
WINAPI C. A queue from C++ has been applied, I repent.

    


    RTP is generated using FFMPEG with the following command :
ffmpeg.exe -f gdigrab -framerate 25 -i desktop -s 853x480 -b:v 120000 -c:v libx264 -f rtp rtp ://127.0.0.1:8080

    


    Parsing the incoming RTP/h264 stream and converting it to an RGB matrix.

    


    video_H264_decode.h

    


    #pragma once
#ifndef _VIDEO_H264_DECODE_SEND_H // Блокируем повторное включение этого модуля
#define _VIDEO_H264_DECODE_SEND_H
//******************************************************************************
// Section include
//******************************************************************************
#include "main.h"
#include 
//******************************************************************************
// Constants
//******************************************************************************

//******************************************************************************
// Type
//******************************************************************************
typedef struct {
    unsigned char* data;
    int size;
}RTPData_DType;

typedef struct 
{
    union
    {
        struct
        {
            char V:2;               //Версия
            char P:1;               //заполнение
            char X:1;               //расширение
            char CC:4;              //количество CSRC

            char M:1;               //маркер (флаг последнего пакета AU),
            char PT:7;              //полезная нагрузка (тип данных носителя полезной нагрузки RTP, H264 = 96)

            short sequence_number;  //Порядковый номер: порядковый номер пакета RTP, увеличенный на 1.
            int time_stamp;         //временная метка выборки медиа. 
            int SSRC;               //Пакет данных имеет одинаковое происхождение.
        };
        unsigned char data[12];
    };
}RTPHeader_DType;
//******************************************************************************
// Global var
//******************************************************************************

//******************************************************************************
// Local function prototype
//******************************************************************************
UCHAR rtp_H264_recive_init(void);
UCHAR RTPStop(void);
//******************************************************************************
// Macros
//******************************************************************************
#define BYTE2_SWAP(X)   ((((short)(X) & 0xff00) >> 8) |(((short)(X) & 0x00ff) << 8))
#endif
//******************************************************************************
// ENF OF FILE
//******************************************************************************


    


    video_H264_decode.c

    


    //******************************************************************************&#xA;//include&#xA;//******************************************************************************&#xA;#include "main.h"&#xA;/*#include "video_H264_decode.h"&#xA;#include &#xA;#include &#xA;#include <chrono>&#xA;#include &#xA;&#xA;#pragma comment(lib, "ws2_32.lib")&#xA;#include */&#xA;#include <iostream>&#xA;#include <queue>&#xA;&#xA;extern "C" {&#xA;#include "libavformat/avformat.h"&#xA;#include "libavfilter/avfilter.h"&#xA;#include "libavdevice/avdevice.h"&#xA;#include "libswscale/swscale.h"&#xA;#include "libswresample/swresample.h"&#xA;#include "libpostproc/postprocess.h"&#xA;#include "libavcodec/avcodec.h"&#xA;}&#xA;&#xA;#pragma comment(lib,"avcodec.lib")&#xA;#pragma comment(lib,"avdevice.lib")&#xA;#pragma comment(lib,"avfilter.lib")&#xA;#pragma comment(lib,"avformat.lib")&#xA;#pragma comment(lib,"avutil.lib")&#xA;#pragma comment(lib,"postproc.lib")&#xA;#pragma comment(lib,"swresample.lib")&#xA;#pragma comment(lib,"swscale.lib")&#xA;&#xA;#pragma warning(disable: 4996)&#xA;//******************************************************************************&#xA;// Section for determining the variables used in the module&#xA;//******************************************************************************&#xA;//------------------------------------------------------------------------------&#xA;// Global&#xA;//------------------------------------------------------------------------------&#xA;&#xA;//------------------------------------------------------------------------------&#xA;// Local&#xA;//------------------------------------------------------------------------------&#xA;const int inteval = 0x01000000;&#xA;BOOL FlagRTPActive = TRUE;&#xA;&#xA;HANDLE hMutexRTPRecive;&#xA;HANDLE hSemaphoreRTP;&#xA;HANDLE hTreadRTPRecive;&#xA;HANDLE hTreadRTPDecode;&#xA;&#xA;&#xA;SOCKET RTPSocket; //socket UDP RTP&#xA;RTPData_DType packet;&#xA;RTPData_DType FU_buffer = { 0 };&#xA;&#xA;std::queue q;&#xA;std::set<int> seq;&#xA;&#xA;AVFormatContext* pAVFormatContext;&#xA;AVCodecContext* pAVCodecContext;&#xA;const AVCodec* pAVCodec;&#xA;AVFrame* pAVFrame;&#xA;AVFrame* AVFrameRGG;&#xA;SwsContext* pSwsContext;&#xA;AVPacket *pAVPacket;&#xA;AVCodecParserContext* pAVCodecParserContext;&#xA;&#xA;UINT port;&#xA;//******************************************************************************&#xA;// Section of prototypes of local functions&#xA;//******************************************************************************&#xA;DWORD WINAPI rtp_H264_recive_Procedure(CONST LPVOID lpParam);&#xA;DWORD WINAPI rtp_decode_Procedure(CONST LPVOID lpParam);&#xA;char RTPSocketInit(void);&#xA;void RTPPacketParser(void);&#xA;char RTPSocketRecive(void);&#xA;void Decode_NaluToAVFrameRGG();&#xA;//******************************************************************************&#xA;// Section of the description of functions&#xA;//******************************************************************************&#xA;UCHAR rtp_H264_recive_init(void)&#xA;{&#xA;    hSemaphoreRTP = CreateSemaphore(&#xA;        NULL,           // default security attributes&#xA;        0,              // initial count&#xA;        1,              // maximum count&#xA;        NULL);          // unnamed semaphore&#xA;&#xA;    hMutexRTPRecive = CreateMutex(&#xA;        NULL,              // default security attributes&#xA;        FALSE,             // initially not owned&#xA;        NULL);             // unnamed mutex&#xA;&#xA;    hTreadRTPRecive = CreateThread(NULL, NULL, rtp_H264_recive_Procedure, NULL, NULL, NULL);&#xA;    hTreadRTPDecode = CreateThread(NULL, NULL, rtp_decode_Procedure, NULL, NULL, NULL);&#xA;&#xA;    return 0;&#xA;}&#xA;//------------------------------------------------------------------------------&#xA;UCHAR RTPStop(void)&#xA;{&#xA;    FlagRTPActive = FALSE;&#xA;&#xA;    if (hSemaphoreRTP) CloseHandle(hSemaphoreRTP);&#xA;    if (hMutexRTPRecive) CloseHandle(hMutexRTPRecive);&#xA;    if (hTreadRTPRecive) CloseHandle(hTreadRTPRecive);&#xA;&#xA;    closesocket(RTPSocket);  &#xA;&#xA;    return 0;&#xA;}&#xA;//------------------------------------------------------------------------------&#xA;DWORD WINAPI rtp_H264_recive_Procedure(CONST LPVOID lpParam)&#xA;{&#xA;    while (RTPSocketInit() == 0)&#xA;        Sleep(2000);&#xA;       &#xA;    while (1)&#xA;    {&#xA;        RTPSocketRecive();&#xA;        RTPPacketParser();&#xA;        ReleaseSemaphore(hSemaphoreRTP, 1, NULL);&#xA;    }&#xA;}&#xA;//------------------------------------------------------------------------------&#xA;DWORD WINAPI rtp_decode_Procedure(CONST LPVOID lpParam)&#xA;{&#xA;    port = param.Option.VideoPort;&#xA;&#xA;    pAVPacket = av_packet_alloc();&#xA;    if (!pAVPacket)&#xA;    {&#xA;        MessageBox(NULL, L"ERROR Could not allocate pAVPacket", L"Init decoder error", MB_OK | MB_ICONERROR);&#xA;        exit(1);&#xA;    }&#xA;    av_init_packet(pAVPacket);&#xA;&#xA;    /* find the MPEG-1 video decoder */&#xA;    pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H264);&#xA;    if (!pAVCodec)&#xA;    {&#xA;        MessageBox(NULL, L"ERROR Codec not found", L"Init decoder error", MB_OK | MB_ICONERROR);&#xA;        exit(1);&#xA;    }&#xA;&#xA;    pAVCodecParserContext = av_parser_init(pAVCodec->id);&#xA;    if (!pAVCodecParserContext)&#xA;    {&#xA;        MessageBox(NULL, L"ERROR Parser not found", L"Init decoder error", MB_OK | MB_ICONERROR);&#xA;        exit(1);&#xA;    }&#xA;&#xA;    pAVCodecContext = avcodec_alloc_context3(pAVCodec);&#xA;    if (!pAVCodecContext) &#xA;    {&#xA;        MessageBox(NULL, L"ERROR Could not allocate video codec context", L"Init decoder error", MB_OK | MB_ICONERROR);&#xA;        exit(1);&#xA;    }&#xA;               &#xA;    if (avcodec_open2(pAVCodecContext, pAVCodec, NULL) &lt; 0)&#xA;    {&#xA;        MessageBox(NULL, L"ERROR Could not open codec", L"Init decoder error", MB_OK | MB_ICONERROR);&#xA;        exit(1);&#xA;    }&#xA;&#xA;    pAVFrame = av_frame_alloc();&#xA;    if (!pAVFrame) &#xA;    {&#xA;        MessageBox(NULL, L"ERROR Could not allocate video frame", L"Init decoder error", MB_OK | MB_ICONERROR);&#xA;        exit(1);&#xA;    }    &#xA;    &#xA;    while (FlagRTPActive)&#xA;    {&#xA;        if(port != param.Option.VideoPort)&#xA;            closesocket(RTPSocket);&#xA;&#xA;        WaitForSingleObject(hSemaphoreRTP, 500);        &#xA;        Decode_NaluToAVFrameRGG();&#xA;    }&#xA;&#xA;    avformat_free_context(pAVFormatContext);&#xA;    av_frame_free(&amp;pAVFrame);&#xA;    avcodec_close(pAVCodecContext);&#xA;    av_packet_free(&amp;pAVPacket);&#xA;&#xA;    &#xA;    if (hTreadRTPDecode) CloseHandle(hTreadRTPDecode);&#xA;&#xA;    return 0;&#xA;}&#xA;&#xA;//------------------------------------------------------------------------------&#xA;char RTPSocketInit(void)&#xA;{    &#xA;    sockaddr_in RTPSocketAddr;&#xA;    &#xA;    RTPSocketAddr.sin_family = AF_INET;&#xA;    RTPSocketAddr.sin_addr.s_addr = htonl(INADDR_ANY);&#xA;    RTPSocketAddr.sin_port = htons(param.Option.VideoPort);&#xA;&#xA;    RTPSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);&#xA;    if (RTPSocket == INVALID_SOCKET)&#xA;    {&#xA;        MessageBox(NULL, L"ERROR Invalid RTP Socket", L"UDP Socket", MB_OK | MB_ICONERROR);&#xA;        return 0;&#xA;    }&#xA;&#xA;    int option = 12000;&#xA;    if (setsockopt(RTPSocket, SOL_SOCKET, SO_RCVBUF, (char*)&amp;option, sizeof(option)) &lt; 0)&#xA;    {&#xA;        printf("setsockopt failed\n");&#xA;&#xA;    }&#xA;&#xA;    /*option = TRUE;&#xA;    if (setsockopt(RTPSocket, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (char*)&amp;option, sizeof(option)) &lt; 0)&#xA;    {&#xA;        printf("setsockopt failed\n");&#xA;&#xA;    }*/&#xA;&#xA;    if (bind(RTPSocket, (sockaddr*)&amp;RTPSocketAddr, sizeof(RTPSocketAddr)) == SOCKET_ERROR)&#xA;    {&#xA;        MessageBox(NULL, L"ERROR bind", L"UDP Socket", MB_OK | MB_ICONERROR);&#xA;        closesocket(RTPSocket);&#xA;        return 0;&#xA;    }&#xA;    return 1;&#xA;}&#xA;&#xA;//------------------------------------------------------------------------------&#xA;char RTPSocketRecive(void)&#xA;{&#xA;    static char RecvBuf[2000];&#xA;    static int BufLen = 2000;&#xA;&#xA;    int iResult = 0;&#xA;    struct sockaddr_in SenderAddr;&#xA;    int SenderAddrSize = sizeof(SenderAddr);&#xA;    TCHAR szErrorMsg[100];&#xA;&#xA;    iResult = recvfrom(RTPSocket, RecvBuf, BufLen, 0, NULL, NULL);&#xA;    if (iResult == SOCKET_ERROR &amp;&amp; FlagRTPActive == TRUE)&#xA;    {&#xA;        StringCchPrintf(szErrorMsg, 100, L"ERROR recvfrom Ошибка:%d", WSAGetLastError());&#xA;        MessageBox(NULL, szErrorMsg, L"UDP Socket", MB_OK | MB_ICONERROR);&#xA;        return -1;&#xA;    }&#xA;&#xA;    packet.data = (unsigned char*)RecvBuf;&#xA;    packet.size = iResult;&#xA;&#xA;    return 1;&#xA;}&#xA;&#xA;//------------------------------------------------------------------------------&#xA;void RTPPacketParser(void)&#xA;{&#xA;    RTPHeader_DType RTPHeader;&#xA;    RTPData_DType NALU;&#xA;    unsigned char* buffer = packet.data;&#xA;    int pos = 0;&#xA;    static int count = 0;&#xA;    static short lastSequenceNumber = 0xFFFF;&#xA;    short type;&#xA;    char payload_header;&#xA;&#xA;    //read rtp header&#xA;    memcpy(&amp;RTPHeader, buffer, sizeof(RTPHeader));&#xA;    RTPHeader.sequence_number = BYTE2_SWAP(RTPHeader.sequence_number);&#xA;    pos &#x2B;= 12;    &#xA;  &#xA;    if (RTPHeader.X) {&#xA;        //profile extension&#xA;        short define;&#xA;        short length;&#xA;        length = buffer[pos &#x2B; 3];//suppose not so long extension&#xA;        pos &#x2B;= 4;&#xA;        pos &#x2B;= (length * 4);&#xA;    }&#xA;&#xA;    payload_header = buffer[pos];&#xA;    type = payload_header &amp; 0x1f; //Тип полезной нагрузки RTP&#xA;    pos&#x2B;&#x2B;;&#xA;    &#xA;    //STAP-A&#xA;    if (type == 24)&#xA;    {        &#xA;        while (pos &lt; packet.size)&#xA;        {&#xA;            unsigned short NALU_size;&#xA;            memcpy(&amp;NALU_size, buffer &#x2B; pos, 2);&#xA;            NALU_size = BYTE2_SWAP(NALU_size);&#xA;            pos &#x2B;= 2;&#xA;            char NAL_header = buffer[pos];&#xA;            short NAL_type = NAL_header &amp; 0x1f;&#xA;&#xA;            if (NAL_type == 7) &#xA;            {&#xA;                count&#x2B;&#x2B;;&#xA;                //cout&lt;&lt;"SPS, sequence number: "&lt;/cout&lt;&lt;"PPS, sequence number: "&lt;/cout&lt;&lt;"end of sequence, sequence number: "&lt; 0) &#xA;            {&#xA;                NALU.data = (unsigned char*) malloc(NALU_size &#x2B; 4);&#xA;                NALU.size = NALU_size &#x2B; 4;&#xA;                memcpy(NALU.data, &amp;inteval, 4);&#xA;                memcpy(NALU.data &#x2B; 4, &amp;buffer[pos], NALU_size);&#xA;&#xA;                WaitForSingleObject(hMutexRTPRecive, INFINITE);&#xA;                q.push(NALU);&#xA;                ReleaseMutex(hMutexRTPRecive);&#xA;            }&#xA;&#xA;            pos &#x2B;= NALU_size;&#xA;        }&#xA;    }&#xA;    //FU-A Fragmentation unit&#xA;    else if (type == 28)&#xA;    {        &#xA;        //FU header&#xA;        char FU_header = buffer[pos];&#xA;        bool fStart = FU_header &amp; 0x80;&#xA;        bool fEnd = FU_header &amp; 0x40;&#xA;&#xA;        //NAL header&#xA;        char NAL_header = (payload_header &amp; 0xe0) | (FU_header &amp; 0x1f);&#xA;        short NAL_type = FU_header &amp; 0x1f;&#xA;        if (NAL_type == 7) &#xA;        {&#xA;            count&#x2B;&#x2B;;&#xA;            //SPS&#xA;        }&#xA;        else if (NAL_type == 8) &#xA;        {&#xA;            //PPS&#xA;        }&#xA;        else if (NAL_type == 10) &#xA;        {&#xA;            //end of sequence&#xA;        }&#xA;        pos&#x2B;&#x2B;;&#xA;&#xA;        int size = packet.size - pos;&#xA;        &#xA;        if (count > 0)&#xA;        {&#xA;            if (fStart) &#xA;            {&#xA;                if (FU_buffer.size != 0)&#xA;                {&#xA;                    free(FU_buffer.data);&#xA;                    FU_buffer.size = 0;&#xA;                }&#xA;                FU_buffer.data = (unsigned char*)malloc(size &#x2B; 5);&#xA;                if (FU_buffer.data == NULL)&#xA;                    return;&#xA;                FU_buffer.size = size &#x2B; 5;&#xA;                memcpy(FU_buffer.data, &amp;inteval, 4);&#xA;                memcpy(FU_buffer.data &#x2B; 4, &amp;NAL_header, 1);&#xA;                memcpy(FU_buffer.data &#x2B; 5, buffer &#x2B; pos, size);&#xA;            }&#xA;            else&#xA;            {&#xA;                unsigned char* temp = (unsigned char*)malloc(FU_buffer.size &#x2B; size);&#xA;                memcpy(temp, FU_buffer.data, FU_buffer.size);&#xA;                memcpy(temp &#x2B; FU_buffer.size, buffer &#x2B; pos, size);&#xA;                if (FU_buffer.size != 0) free(FU_buffer.data);&#xA;                FU_buffer.data = temp;&#xA;                FU_buffer.size &#x2B;= size;&#xA;            }&#xA;&#xA;            if (fEnd)&#xA;            {&#xA;                NALU.data = (unsigned char*)malloc(FU_buffer.size);&#xA;                NALU.size = FU_buffer.size;&#xA;                memcpy(NALU.data, FU_buffer.data, FU_buffer.size);&#xA;&#xA;                WaitForSingleObject(hMutexRTPRecive, INFINITE);&#xA;                q.push(NALU);&#xA;                ReleaseMutex(hMutexRTPRecive);&#xA;&#xA;                free(FU_buffer.data);&#xA;                FU_buffer.size = 0;&#xA;            }&#xA;        }&#xA;        &#xA;    }&#xA;    else &#xA;    {&#xA;        //other type&#xA;        short NAL_type = type;&#xA;        if (NAL_type == 7) &#xA;        {&#xA;            count&#x2B;&#x2B;;&#xA;            //SPS&#xA;        }&#xA;        else if (NAL_type == 8) &#xA;        {&#xA;            //PPS&#xA;        }&#xA;        else if (NAL_type == 10) &#xA;        {&#xA;            //end of sequence&#xA;        }&#xA;&#xA;        int size = packet.size - pos &#x2B; 1;&#xA;        if (count > 0)&#xA;        {&#xA;            NALU.data = (unsigned char*)malloc(size&#x2B;4);&#xA;            NALU.size = size &#x2B; 4;&#xA;            memcpy(NALU.data, &amp;inteval, 4);&#xA;            memcpy(NALU.data &#x2B; 4, &amp;buffer[12], size);&#xA;&#xA;            WaitForSingleObject(hMutexRTPRecive, INFINITE);&#xA;            q.push(NALU);&#xA;            ReleaseMutex(hMutexRTPRecive);&#xA;        }&#xA;    }&#xA;}&#xA;&#xA;//------------------------------------------------------------------------------&#xA;void Decode_NaluToAVFrameRGG()&#xA;{&#xA;    unsigned char cntNALU;&#xA;    int len = 0;&#xA;    static int size = 0;&#xA;    unsigned char* data = NULL;&#xA;    int ret;&#xA;    RTPData_DType NALU;&#xA;&#xA;    av_frame_unref(pAVFrame);&#xA;    av_packet_unref(pAVPacket);&#xA;&#xA;    while(1)&#xA;    {&#xA;        WaitForSingleObject(hMutexRTPRecive, INFINITE);&#xA;        if (q.empty())&#xA;        {&#xA;            ReleaseMutex(hMutexRTPRecive);&#xA;            break;&#xA;        }&#xA;        NALU = q.front();&#xA;        q.pop();&#xA;        ReleaseMutex(hMutexRTPRecive);&#xA;&#xA;        data = NALU.data;&#xA;        size = NALU.size;&#xA;&#xA;        while(size)&#xA;        {            &#xA;            len = av_parser_parse2(pAVCodecParserContext, pAVCodecContext, &amp;pAVPacket->data, &amp;pAVPacket->size,&#xA;                (uint8_t*)data, size,&#xA;                AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);&#xA;&#xA;            data = len ? data &#x2B; len : data;&#xA;            size -= len;&#xA;            &#xA;            if (pAVPacket->size)&#xA;            {&#xA;                ret = avcodec_send_packet(pAVCodecContext, pAVPacket);&#xA;&#xA;                if(ret == AVERROR_EOF)&#xA;                    MessageBox(NULL, L"the codec has been fully flushed, and there will be no more output frames", L"avcodec_send_packet", MB_OK | MB_ICONERROR);&#xA;&#xA;                if (ret &lt; 0)&#xA;                {&#xA;                    MessageBox(NULL, L"ERROR sending a packet for decoding", L"Decode", MB_OK | MB_ICONERROR);&#xA;                    //exit(1);&#xA;                }&#xA;&#xA;                while (ret >= 0) &#xA;                {&#xA;                    ret = avcodec_receive_frame(pAVCodecContext, pAVFrame);&#xA;                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)&#xA;                        break;&#xA;                    else if (ret &lt; 0) {&#xA;                        MessageBox(NULL, L"ERROR during decoding", L"Decode", MB_OK | MB_ICONERROR);&#xA;                        //exit(1);&#xA;                    }&#xA;                    &#xA;                    AVFrameRGG = av_frame_alloc();                                       &#xA;                    pSwsContext = sws_getContext(pAVCodecContext->width, pAVCodecContext->height, pAVCodecContext->pix_fmt,&#xA;                        pAVCodecContext->width, pAVCodecContext->height, AV_PIX_FMT_RGB24, SWS_SPLINE,&#xA;                        0, 0, 0);&#xA;&#xA;                    sws_scale_frame(pSwsContext, AVFrameRGG, pAVFrame);&#xA;                                        &#xA;                    ImgBufferSet(AVFrameRGG->data[0], pAVCodecContext->height, pAVCodecContext->width, AVFrameRGG->linesize[0]);&#xA;&#xA;                    sws_freeContext(pSwsContext);&#xA;                    av_frame_free(&amp;AVFrameRGG);&#xA;                }&#xA;            }&#xA;        }&#xA;        free(NALU.data);&#xA;        NALU.size = 0;&#xA;    }    &#xA;}&#xA;&#xA;</int></queue></iostream></chrono>

    &#xA;

    It's DECIDED

    &#xA;

  • My SBC Collection

    31 décembre 2023, par Multimedia Mike — General

    Like many computer nerds in the last decade, I have accumulated more than a few single-board computers, or “SBCs”, which are small computers based around a system-on-a-chip (SoC) that nearly always features an ARM CPU at its core. Surprisingly few of these units are Raspberry Pi units, though that brand has come to exemplify and dominate the product category.

    Also, as is the case for many computer nerds, most of these SBCs lay fallow for years at a time. Equipped with an inexpensive lightbox that I procured in the last year, I decided I could at least create glamour shots of various units and catalog them in a blog post.

    While Raspberry Pi still enjoys the most mindshare far and away, and while I do have a few Raspberry Pi units in my inventory, I have always been a bigger fan of the ODROID brand, which works with convenient importers around the world (in the USA, I can vouch for Ameridroid, to whom I’ve forked over a fair amount of cash for these computing toys).

    As mentioned, Raspberry Pi undisputedly has the most mindshare of all these SBC brands and I often wonder why… and then I immediately remind myself that it has the biggest ecosystem, and has a variety of turnkey projects and applications (such as Pi-hole and PiVPN) that promise a lower barrier to entry — as well as a slightly lower price point — than some of these other options. ODROID had a decent ecosystem for awhile, especially considering the monthly ODROID Magazine, though that ceased publication in July 2020. The Raspberry Pi and its variants were famously difficult to come by due to the global chip shortage from 2021-2023. Meanwhile, I had no trouble procuring these boards during the same timeframe.

    So let’s delve into the collection…

    Cubieboard
    The Raspberry Pi came out in 2012 and by 2013 I was somewhat coveting one to hack on. Finally ! An accessible ARM platform to play with. I had heard of the BeagleBoard for years but never tried to get my hands on one. I was thinking about taking the plunge on a new Raspberry Pi, but a colleague told me I should skip that and go with this new hotness called the Cubieboard, based on an Allwinner SoC. The big value-add that this board had vs. a Raspberry Pi was that it had a SATA adapter. Although now that it has been a decade, it only now occurs to me to quander whether it was true SATA or a USB-to-SATA bridge. Looking it up now, I’m led to believe that the SoC supported the functionality natively.

    Anyway, I did get it up and running but never did much with it, thus setting the tone for future SBC endeavors. No photos because I gave it to another tech enthusiast years ago, whose SBC collection dwarfs my own.

    ODROID-XU4
    I can’t recall exactly when or how I first encountered the ODROID brand. I probably read about it on some enthusiast page or another circa 2014 and decided to try one out. I eventually acquired a total of 3 of these ODROID-XU4 units, each with a different case, 1 with a fan and 2 passively-cooled :

    Collection of ODROID-XU4 SBCs

    Collection of ODROID-XU4 SBCs

    This is based on the Samsung Exynos 5422 SoC, the same series as was used in their Note 3 phone released in 2013. It has been a fun chip to play with. The XU4 was also my first introduction to the eMMC storage solution that is commonly supported on the ODROID SBCs (alongside micro-SD). eMMC offers many benefits over SD in terms of read/write speed as well as well as longevity/write cycles. That’s getting less relevant these days, however, as more and more SBCs are being released with direct NVMe SSD support.

    I had initially wanted to make a retro-gaming device built on this platform (see the handheld section later for more meditations on that). In support of this common hobbyist goal, there is this nifty case XU4 case which apes the aesthetic of the Nintendo N64 :

    ODROID-XU4 N64-style case

    ODROID-XU4 N64-style case

    It even has a cool programmable LCD screen. Maybe one day I’ll find a use for it.

    For awhile, one of these XU4 units (likely the noisy, fan-cooled one) was contributing results to the FFmpeg FATE system.

    While it features gigabit ethernet and a USB3 port, I once tried to see if I could get 2 Gbps throughput with the unit using a USB3-gigabit dongle. I had curious results in that the total amount of traffic throughput could never exceed 1 Gbps across both interfaces. I.e., if 1 interface was dealing with 1 Gbps and the other interface tried to run at 1 Gbps, they would both only run at 500 Mbps. That remains a mystery to me since I don’t see that limitation with Intel chips.

    Still, the XU4 has been useful for a variety of projects and prototyping over the years.

    ODROID-HC2 NAS
    I find that a lot of my fellow nerds massively overengineer their homelab NAS setups. I’ll explore this in a future post. For my part, people tend to find my homelab NAS solution slightly underengineered. This is the ODROID-HC2 (the “HC” stands for “Home Cloud”) :

    ODROID-HC2 NAS

    ODROID-HC2 NAS

    It has the same guts as the ODROID-XU4 except no video output and the USB3 function is leveraged for a SATA bridge. This allows you to plug a SATA hard drive directly into the unit :

    ODROID-HC2 NAS uncovered

    ODROID-HC2 NAS uncovered

    Believe it or not, this has been my home NAS solution for something like 6 or 7 years now– I don’t clearly remember when I purchased it and put it into service.

    But isn’t this sort of irresponsible ? What about a failure of the main drive ? That’s why I have an external drive connected for backing up the most important data via rsync :

    ODROID-HC2 NAS backup enclosure

    ODROID-HC2 NAS backup enclosure

    The power consumption can’t be beat– Profiling for a few weeks of average usage worked out to 4.5 kWh for the ODROID-HC2… per month.

    ODROID-C2
    I was on a kick of ordering more SBCs at one point. This is the ODROID-C2, equipped with a 64-bit Amlogic SoC :

    ODROID-C2

    ODROID-C2

    I had this on the FATE farm for awhile, performing 64-bit ARM builds (vs. the XU4’s 32-bit builds). As memory serves, it was unreliable and would occasionally freeze up.

    Here is a view of the eMMC storage through the bottom of the translucent case :

    Bottom of ODROID-C2 with view of eMMC storage

    Bottom of ODROID-C2 with view of eMMC storage

    ODROID-N2+
    Out of all my ODROID SBCs, this is the unit that I long to “get back to” the most– the ODROID-N2+ :

    ODROID-N2+

    ODROID-N2+

    Very capable unit that makes a great little desktop. I have some projects I want to develop using it so that it will force me to have a focused development environment.

    Raspberry Pi
    Eventually, I did break down and get a Raspberry Pi. I had a specific purpose in mind and, much to my surprise, I have stuck to it :

    Original Raspberry Pi

    Original Raspberry Pi

    I was using one of the ODROID-XU4 units as a VPN gateway. Eventually, I wanted to convert the XU4 to something else and I decided to run the VPN gateway as an appliance on the simplest device I could. So I procured this complete hand-me-down unit from eBay and went to work. This was also the first time I discovered the DietPi distribution and this box has been in service running Wireguard via PiVPN for many years.

    I also have a Raspberry Pi 3B+ kicking around somewhere. I used it as a Steam Link device for awhile.

    SOPINE + Baseboard
    Also procured when I was on this “let’s buy random SBCs” kick. The Pine64 SOPINE is actually a compute module that comes in the form factor of a memory module.

    Pine64 SOPINE Compute Module

    Pine64 SOPINE Compute Module

    Back to using Allwinner SoCs. In order to make this thing useful, you need to place it in something. It’s possible to get a mini-ITX form factor board that can accommodate 7 of these modules. Before going to that extreme, there is this much simpler baseboard which can also use eMMC for storage.

    Baseboard with SOPINE, eMMC, and heat sinks

    Baseboard with SOPINE, eMMC, and heat sinks

    I really need to find an appropriate case for this one as it currently performs its duty while sitting on an anti-static bag.

    NanoPi NEO3
    I enjoy running the DietPi distribution on many of these SBCs (as it’s developed not just for Raspberry Pi). I have also found their website to be a useful resource for discovering new SBCs. That’s how I found the NanoPi series and zeroed in on this NEO3 unit, sporting a Rockchip SoC, and photographed here with some American currency in order to illustrate its relative size :

    NanoPi NEO3

    NanoPi NEO3

    I often forget about this computer because it’s off in another room, just quietly performing its assigned duty.

    MangoPi MQ-Pro
    So far, I’ve heard of these fruits prepending the Greek letter pi for naming small computing products :

    • Raspberry – the O.G.
    • Banana – seems to be popular for hobbyist router/switches
    • Orange
    • Atomic
    • Nano
    • Mango

    Okay, so the AtomicPi and NanoPi names don’t really make sense considering the fruit convention.

    Anyway, the newest entry is the MangoPi. These showed up on Ameridroid a few months ago. There are 2 variants : the MQ-Pro and the MQ-Quad. I picked one and rolled with it.

    MangoPi MQ-Pro pieces arrive

    MangoPi MQ-Pro pieces arrive

    When it arrived, I unpacked it, assembled the pieces, downloaded a distro, tossed that on a micro-SD card, connected a monitor and keyboard to it via its USB-C port, got the distro up and running, configured the wireless networking with a static IP address and installed sshd, and it was ready to go as a headless server for an edge application.

    MangoPi MQ-Pro components, ready for assembly

    MangoPi MQ-Pro components, ready for assembly

    The unit came with no instructions that I can recall. After I got it set up, I remember thinking, “What is wrong with me ? Why is it that I just know how to do all of this without any documentation ?”

    MangoPi MQ-Pro in first test

    MangoPi MQ-Pro in first test

    Only after I got it up and running and poked around a bit did I realize that this SBC doesn’t have an ARM SoC– it’s a RISC-V SoC. It uses the Allwinner D1, so it looks like I came full circle back to Allwinner.

    MangoPi MQ-Pro with more US coinage for scale

    MangoPi MQ-Pro with more US coinage for scale

    So I now have my first piece of RISC-V hobbyist kit, although I learned recently from Kostya that it’s not that great for multimedia.

    Handheld Gaming Units
    The folks at Hardkernel have also produced a series of handheld retro-gaming devices called ODROID-GO. The first one resembled the original Nintendo Game Boy, came as a kit to be assembled, and emulated 5 classic consoles. It also had some hackability to it. Quite a cool little device, and inexpensive too. I have since passed it along to another gaming enthusiast.

    Later came the ODROID-GO Advance, also a kit, but emulating more devices. I was extremely eager to get my hands on this since it could emulate SNES in addition to NES. It also features a headphone jack, unlike the earlier model. True to form, after I received mine, it took me about 13 months before I got around to assembling it. After that, the biggest challenge I had was trying to find an appropriate case for it.

    ODROID-GO Advance with case and headphones

    ODROID-GO Advance with case and headphones

    Even though it may try to copy the general aesthetic and form factor of the Game Boy Advance, cases for the GBA don’t fit this correctly.

    Further, Hardkernel have also released the ODROID-GO Super and Ultra models that do more and more. The Advance, Super, and Ultra models have powerful SoCs and feature much more hackability than the first ODROID-GO model.

    I know that the guts of the Advance have been used in other products as well. The same is likely true for the Super and Ultra.

    Ultimately, the ODROID-GO Advance was just another project I assembled and then set aside since I like the idea of playing old games much more than actually doing it. Plus, the fact has finally crystalized in my mind over the past few years that I have never enjoyed handheld gaming and likely will never enjoy handheld gaming, even after I started wearing glasses. Not that I’m averse to old Game Boy / Color / Advance games, but if I’m going to play them, I’d rather emulate them on a large display.

    The Future
    In some of my weaker moments, I consider ordering up certain Banana Pi products (like the Banana Pi BPI-R2) with a case and doing my own router tricks using some open source router/firewall solution. And then I remind myself that my existing prosumer-type home router is doing just fine. But maybe one day…

    The post My SBC Collection first appeared on Breaking Eggs And Making Omelettes.