text:
Today we will explain the steps and algorithm of how to convert PDF to CSV asynchronously and how to make it work in your application. PDF.co Web API was made to help with PDF to CSV API in PHP. PDF.co Web API is the flexible Web API that includes full set of functions from e-signature requests to data extraction, OCR, images recognition, pdf splitting and pdf splitting. Can also generate barcodes and read barcodes from images, scans and pdf.

PHP code snippet like this for PDF.co Web API works best when you need to quickly implement PDF to CSV API in your PHP application. Follow the instruction and copy - paste code for PHP into your project's code editor. Use of PDF.co Web API in PHP is also explained in the documentation included along with the product.

Our website provides free trial version of PDF.co Web API that includes source code samples to help with your PHP project.

## REQUEST FREE TECH SUPPORT

[Click here to get in touch](https://bytescout.zendesk.com/hc/en-us/requests/new?subject=PDF.co%20Web%20API%20Question)

or just send email to [support@bytescout.com](mailto:support@bytescout.com?subject=PDF.co%20Web%20API%20Question)

## ON-PREMISE OFFLINE SDK

[Get Your 60 Day Free Trial](https://bytescout.com/download/web-installer?utm_source=github-readme)
[Explore Documentation](https://bytescout.com/documentation/index.html?utm_source=github-readme)
[Explore Source Code Samples](https://github.com/bytescout/ByteScout-SDK-SourceCode/)
[Sign Up For Online Training](https://academy.bytescout.com/)

## ON-DEMAND REST WEB API

[Get your API key](https://app.pdf.co/signup?utm_source=github-readme)
[Security](https://pdf.co/security)
[Explore Web API Documentation](https://apidocs.pdf.co?utm_source=github-readme)
[Explore Web API Samples](https://github.com/bytescout/ByteScout-SDK-SourceCode/tree/master/PDF.co%20Web%20API)

## VIDEO REVIEW

[https://www.youtube.com/watch?v=NEwNs2b9YN8](https://www.youtube.com/watch?v=NEwNs2b9YN8)

<!-- code block begin -->

##### **pdf-to-csv-async.php:**

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Cloud API asynchronous "PDF To CSV" job example (allows to avoid timeout errors).</title>
</head>
<body>

<?php

// Cloud API asynchronous "PDF To CSV" job example.
// Allows to avoid timeout errors when processing huge or scanned PDF documents.

// The authentication key (API Key).
// Get your own by registering at https://app.pdf.co
$apiKey = "***********************************";

// Direct URL of source PDF file. Check another example if you need to upload a local file to the cloud.
$sourceFileUrl = "https://bytescout-com.s3.amazonaws.com/files/demo-files/cloud-api/pdf-to-text/long-processed-document.pdf";
// Comma-separated list of page indices (or ranges) to process. Leave empty for all pages. Example: '0,2-5,7-'.
$pages = "";
// PDF document password. Leave empty for unprotected documents.
$password = "";

// Prepare URL for `PDF To CSV` API call
$url = "https://api.pdf.co/v1/pdf/convert/to/csv";

// Prepare requests params
$parameters = array();
$parameters["url"] = $sourceFileUrl;
$parameters["password"] = $password;
$parameters["pages"] = $pages;
$parameters["async"] = true;  // (!) Make asynchronous job

// Create Json payload
$data = json_encode($parameters);

// Create request
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, array("x-api-key: " . $apiKey, "Content-type: application/json"));
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

// Execute request
$result = curl_exec($curl);

if (curl_errno($curl) == 0)
{
    $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

    if ($status_code == 200)
    {
        $json = json_decode($result, true);

        if (!isset($json["error"]) || $json["error"] == false)
        {
            // URL of generated CSV file that will available after the job completion
            $resultFileUrl = $json["url"];
            // Asynchronous job ID
            $jobId = $json["jobId"];

            // Check the job status in a loop
            do
            {
                $status = CheckJobStatus($jobId, $apiKey); // Possible statuses: "working", "failed", "aborted", "success".

                // Display timestamp and status (for demo purposes)
                echo "<p>" . date(DATE_RFC2822) . ": " . $status . "</p>";

                if ($status == "success")
                {
                    // Display link to the file with conversion results
                    echo "<div>## Conversion Result:<a href='" . $resultFileUrl . "' target='_blank'>" . $resultFileUrl . "</a></div>";
                    break;
                }
                else if ($status == "working")
                {
                    // Pause for a few seconds
                    sleep(3);
                }
                else
                {
                    echo $status . "<br/>";
                    break;
                }
            }
            while (true);
        }
        else
        {
            // Display service reported error
            echo "<p>Error: " . $json["message"] . "</p>";
        }
    }
    else
    {
        // Display request error
        echo "<p>Status code: " . $status_code . "</p>";
        echo "<p>" . $result . "</p>";
    }
}
else
{
    // Display CURL error
    echo "Error: " . curl_error($curl);
}

// Cleanup
curl_close($curl);

function CheckJobStatus($jobId, $apiKey)
{
    $status = null;

	// Create URL
    $url = "https://api.pdf.co/v1/job/check";

    // Prepare requests params
    $parameters = array();
    $parameters["jobid"] = $jobId;

    // Create Json payload
    $data = json_encode($parameters);

    // Create request
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_HTTPHEADER, array("x-api-key: " . $apiKey, "Content-type: application/json"));
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

    // Execute request
    $result = curl_exec($curl);

    if (curl_errno($curl) == 0)
    {
        $status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

        if ($status_code == 200)
        {
            $json = json_decode($result, true);

            if (!isset($json["error"]) || $json["error"] == false)
            {
                $status = $json["status"];
            }
            else
            {
                // Display service reported error
                echo "<p>Error: " . $json["message"] . "</p>";
            }
        }
        else
        {
            // Display request error
            echo "<p>Status code: " . $status_code . "</p>";
            echo "<p>" . $result . "</p>";
        }
    }
    else
    {
        // Display CURL error
        echo "Error: " . curl_error($curl);
    }

    // Cleanup
    curl_close($curl);

    return $status;
}

?>

</body>
</html>
```

<!-- code block end -->


text:
#include <xc.h>
#include <system.h>
#include <system_config.h>
#include <usb/usb.h>
#include <leds.h>
#include <buttons.h>
#include <adc.h>

/** CONFIGURATION Bits **********************************************/
#pragma config PLLSEL   = PLL3X     // PLL Selection (3x clock multiplier)
#pragma config CFGPLLEN = OFF       // PLL Enable Configuration bit (PLL Disabled (firmware controlled))
#pragma config CPUDIV   = NOCLKDIV  // CPU System Clock Postscaler (CPU uses system clock (no divide))
#pragma config LS48MHZ  = SYS48X8   // Low Speed USB mode with 48 MHz system clock (System clock at 48 MHz, USB clock divider is set to 8)
#pragma config FOSC     = INTOSCIO  // Oscillator Selection (Internal oscillator)
#pragma config PCLKEN   = OFF       // Primary Oscillator Shutdown (Primary oscillator shutdown firmware controlled)
#pragma config FCMEN    = OFF       // Fail-Safe Clock Monitor (Fail-Safe Clock Monitor disabled)
#pragma config IESO     = OFF       // Internal/External Oscillator Switchover (Oscillator Switchover mode disabled)
#pragma config nPWRTEN  = OFF       // Power-up Timer Enable (Power up timer disabled)
#pragma config BOREN    = SBORDIS   // Brown-out Reset Enable (BOR enabled in hardware (SBOREN is ignored))
#pragma config BORV     = 190       // Brown-out Reset Voltage (BOR set to 1.9V nominal)
#pragma config nLPBOR   = ON        // Low-Power Brown-out Reset (Low-Power Brown-out Reset enabled)
#pragma config WDTEN    = SWON      // Watchdog Timer Enable bits (WDT controlled by firmware (SWDTEN enabled))
#pragma config WDTPS    = 32768     // Watchdog Timer Postscaler (1:32768)
#pragma config CCP2MX   = RC1       // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN   = OFF       // PORTB A/D Enable bit (PORTB<5:0> pins are configured as digital I/O on Reset)
#pragma config T3CMX    = RC0       // Timer3 Clock Input MUX bit (T3CKI function is on RC0)
#pragma config SDOMX    = RC7       // SDO Output MUX bit (SDO function is on RC7)
#pragma config MCLRE    = ON        // Master Clear Reset Pin Enable (MCLR pin enabled; RE3 input disabled)
#pragma config STVREN   = ON        // Stack Full/Underflow Reset (Stack full/underflow will cause Reset)
#pragma config LVP      = OFF       // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config ICPRT    = OFF       // Dedicated In-Circuit Debug/Programming Port Enable (ICPORT disabled)
#pragma config XINST    = OFF       // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled)
#pragma config CP0      = OFF       // Block 0 Code Protect (Block 0 is not code-protected)
#pragma config CP1      = OFF       // Block 1 Code Protect (Block 1 is not code-protected)
#pragma config CP2      = OFF       // Block 2 Code Protect (Block 2 is not code-protected)
#pragma config CP3      = OFF       // Block 3 Code Protect (Block 3 is not code-protected)
#pragma config CPB      = OFF       // Boot Block Code Protect (Boot block is not code-protected)
#pragma config CPD      = OFF       // Data EEPROM Code Protect (Data EEPROM is not code-protected)
#pragma config WRT0     = OFF       // Block 0 Write Protect (Block 0 (0800-1FFFh) is not write-protected)
#pragma config WRT1     = OFF       // Block 1 Write Protect (Block 1 (2000-3FFFh) is not write-protected)
#pragma config WRT2     = OFF       // Block 2 Write Protect (Block 2 (04000-5FFFh) is not write-protected)
#pragma config WRT3     = OFF       // Block 3 Write Protect (Block 3 (06000-7FFFh) is not write-protected)
#pragma config WRTC     = OFF       // Configuration Registers Write Protect (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB     = OFF       // Boot Block Write Protect (Boot block (0000-7FFh) is not write-protected)
#pragma config WRTD     = OFF       // Data EEPROM Write Protect (Data EEPROM is not write-protected)
#pragma config EBTR0    = OFF       // Block 0 Table Read Protect (Block 0 is not protected from table reads executed in other blocks)
#pragma config EBTR1    = OFF       // Block 1 Table Read Protect (Block 1 is not protected from table reads executed in other blocks)
#pragma config EBTR2    = OFF       // Block 2 Table Read Protect (Block 2 is not protected from table reads executed in other blocks)
#pragma config EBTR3    = OFF       // Block 3 Table Read Protect (Block 3 is not protected from table reads executed in other blocks)
#pragma config EBTRB    = OFF       // Boot Block Table Read Protect (Boot block is not protected from table reads executed in other blocks)

/*********************************************************************
* Function: void SYSTEM_Initialize( SYSTEM_STATE state )
*
* Overview: Initializes the system.
*
* PreCondition: None
*
* Input:  SYSTEM_STATE - the state to initialize the system into
*
* Output: None
*
********************************************************************/
void SYSTEM_Initialize( SYSTEM_STATE state )
{
    switch(state)
    {
        case SYSTEM_STATE_USB_START:

            //Configure oscillator settings for clock settings compatible with USB
            //operation.  Note: Proper settings depends on USB speed (full or low).
            #if(USB_SPEED_OPTION == USB_FULL_SPEED)
                OSCTUNE = 0x80; //3X PLL ratio mode selected
                OSCCON = 0x70;  //Switch to 16MHz HFINTOSC
                OSCCON2 = 0x10; //Enable PLL, SOSC, PRI OSC drivers turned off
                while(OSCCON2bits.PLLRDY != 1);   //Wait for PLL lock
                ACTCON = 0x90;  //Enable active clock tuning for USB operation
            #endif

            LED_Enable(LED_USB_DEVICE_STATE);
            LED_Enable(LED_USB_DEVICE_HID_CUSTOM);

            BUTTON_Enable(BUTTON_USB_DEVICE_HID_CUSTOM);

            ADC_SetConfiguration(ADC_CONFIGURATION_DEFAULT);
            ADC_Enable(ADC_CHANNEL_POTENTIOMETER);
            break;

        case SYSTEM_STATE_USB_SUSPEND:
            break;

        case SYSTEM_STATE_USB_RESUME:
            break;
    }
}

#if defined(__XC8)
void interrupt SYS_InterruptHigh(void)
{
    #if defined(USB_INTERRUPT)
        USBDeviceTasks();
    #endif
}
#else
    void YourHighPriorityISRCode();
    void YourLowPriorityISRCode();

    //On PIC18 devices, addresses 0x00, 0x08, and 0x18 are used for
    //the reset, high priority interrupt, and low priority interrupt
    //vectors.  However, the current Microchip USB bootloader
    //examples are intended to occupy addresses 0x00-0x7FF or
    //0x00-0xFFF depending on which bootloader is used.  Therefore,
    //the bootloader code remaps these vectors to new locations
    //as indicated below.  This remapping is only necessary if you
    //wish to program the hex file generated from this project with
    //the USB bootloader.  If no bootloader is used, edit the
    //usb_config.h file and comment out the following defines:
    //#define PROGRAMMABLE_WITH_USB_HID_BOOTLOADER
    //#define PROGRAMMABLE_WITH_USB_LEGACY_CUSTOM_CLASS_BOOTLOADER

    #if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)
            #define REMAPPED_RESET_VECTOR_ADDRESS			0x1000
            #define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS	0x1008
            #define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS	0x1018
    #elif defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
            #define REMAPPED_RESET_VECTOR_ADDRESS			0x800
            #define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS	0x808
            #define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS	0x818
    #else
            #define REMAPPED_RESET_VECTOR_ADDRESS			0x00
            #define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS	0x08
            #define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS	0x18
    #endif

    #if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
    extern void _startup (void);        // See c018i.c in your C18 compiler dir
    #pragma code REMAPPED_RESET_VECTOR = REMAPPED_RESET_VECTOR_ADDRESS
    void _reset (void)
    {
        _asm goto _startup _endasm
    }
    #endif

    #pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS
    void Remapped_High_ISR (void)
    {
         _asm goto YourHighPriorityISRCode _endasm
    }
    #pragma code REMAPPED_LOW_INTERRUPT_VECTOR = REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS
    void Remapped_Low_ISR (void)
    {
         _asm goto YourLowPriorityISRCode _endasm
    }

    #if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_MCHPUSB_BOOTLOADER)
    //Note: If this project is built while one of the bootloaders has
    //been defined, but then the output hex file is not programmed with
    //the bootloader, addresses 0x08 and 0x18 would end up programmed with 0xFFFF.
    //As a result, if an actual interrupt was enabled and occured, the PC would jump
    //to 0x08 (or 0x18) and would begin executing "0xFFFF" (unprogrammed space).  This
    //executes as nop instructions, but the PC would eventually reach the REMAPPED_RESET_VECTOR_ADDRESS
    //(0x1000 or 0x800, depending upon bootloader), and would execute the "goto _startup".  This
    //would effective reset the application.

    //To fix this situation, we should always deliberately place a
    //"goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS" at address 0x08, and a
    //"goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS" at address 0x18.  When the output
    //hex file of this project is programmed with the bootloader, these sections do not
    //get bootloaded (as they overlap the bootloader space).  If the output hex file is not
    //programmed using the bootloader, then the below goto instructions do get programmed,
    //and the hex file still works like normal.  The below section is only required to fix this
    //scenario.
    #pragma code HIGH_INTERRUPT_VECTOR = 0x08
    void High_ISR (void)
    {
         _asm goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS _endasm
    }
    #pragma code LOW_INTERRUPT_VECTOR = 0x18
    void Low_ISR (void)
    {
         _asm goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS _endasm
    }
    #endif	//end of "#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_LEGACY_CUSTOM_CLASS_BOOTLOADER)"

    #pragma code

    //These are your actual interrupt handling routines.
    #pragma interrupt YourHighPriorityISRCode
    void YourHighPriorityISRCode()
    {
            //Check which interrupt flag caused the interrupt.
            //Service the interrupt
            //Clear the interrupt flag
            //Etc.
    #if defined(USB_INTERRUPT)
            USBDeviceTasks();
    #endif

    }	//This return will be a "retfie fast", since this is in a #pragma interrupt section
    #pragma interruptlow YourLowPriorityISRCode
    void YourLowPriorityISRCode()
    {
            //Check which interrupt flag caused the interrupt.
            //Service the interrupt
            //Clear the interrupt flag
            //Etc.

    }	//This return will be a "retfie", since this is in a #pragma interruptlow section
#endif

redpajama_set_name: RedPajamaGithub


text:
!> \file randmod.f90
!! Innocent defined module for normal random numbers
!<

!> Module for random numbers generation
!! used for Data Assimilation purpose
!! It is mandatory to call init_normal_rand to initialize the module before using it
!! One may call finilize_normal_rand to finilize the module
!! The method used here consists in a quasi inversion of the CDF. The space is subdivided into small intervals. Given a uniform random value between 0 and 1, the CDF of the normal distribution is inverted at the value. When the inversion falls into an interval, the middle of the interval is considered as the associated normal random value.
!<
MODULE randmod
  USE general_constant
  USE general_tools, only: search_pos_inc, init_random_seed
  USE debug_tools
IMPLICIT NONE
  !> default number of possible values at the right of the mean
  INTEGER, PARAMETER :: DEFAULT_NRIGHT = 4096!1024

  !> user defined type to keep and manage information on a random numbers generator
  TYPE rng_environment
    LOGICAL :: l_init = .FALSE. !< Says if the radom number generator environment is initialized or not
    LOGICAL :: l_stat = .FALSE. !< Save statistics (frequency) for analysis? Statistics can be used to check the Gaussianity of a gaussian generator
    !for statistics
    INTEGER :: i_nCall  = 0     !< number of call to the generator, used for statistics
    INTEGER :: i_lb, i_ub       !< bounds of the statistics array
    REAL(KIND=cp), DIMENSION(:), ALLOCATABLE :: ra_rands!<Vector of possible values
    REAL(KIND=cp), DIMENSION(:), ALLOCATABLE :: ra_cdf  !< CDF at possibles values

    REAL(KIND=cp), DIMENSION(:), ALLOCATABLE :: ra_pdf  !< PDF of the generator
    INTEGER, DIMENSION(:), ALLOCATABLE :: ia_stats !< frequency of random numbers generated in a set of predefined intervals
  END TYPE rng_environment

  !> user type to store parameter of random Gaussian number generator
  TYPE, PRIVATE :: gaussian_param
    INTEGER :: i_nright = 0     !< Number of Number of possible values (greater than the mean) that can be generated
    REAL(KIND=cp) :: r_radius   !< Distance from the mean to the maximum/minimum number that can be generated
    REAL(KIND=cp) :: r_mean     !< mean of the distribution
    REAL(KIND=cp) :: r_sigma    !< Standard deviation of the distribution
    REAL(KIND=cp) :: r_step     !< step between possible successive values
    TYPE(rng_environment) :: t_env  !< environment of the random number generator
  END TYPE gaussian_param

  INTERFACE normal_random
    MODULE PROCEDURE normal_random_scalar
    MODULE PROCEDURE normal_random_vector
  END INTERFACE normal_random

  TYPE(gaussian_param), PRIVATE, SAVE :: tm_gaussp !< parameter for the Gaussian generator

CONTAINS

!--- info
  !> \brief Initializes the environment for a random number generator
  !! \param[in,out] td_rnge environment to be initialized
  !! \param[in] id_lb lower bound index of the environments arrays
  !! \param[in] id_ub upper bound index of the environments arrays
  !! \param[in] stat status of the statistics
  !<
  SUBROUTINE init_rng_environment(td_rnge, id_lb, id_ub, stat)
    TYPE(rng_environment), INTENT(INOUT) :: td_rnge
    INTEGER, INTENT(IN) :: id_lb, id_ub
    LOGICAL, INTENT(IN), OPTIONAL :: stat

    IF ( PRESENT( stat) ) THEN
      td_rnge%l_stat = stat
    ELSE
      td_rnge%l_stat = .FALSE.
    END IF
    td_rnge%i_lb = id_lb
    td_rnge%i_ub = id_ub
    IF( ALLOCATED(td_rnge%ra_rands) )DEALLOCATE(td_rnge%ra_rands)
    IF( ALLOCATED(td_rnge%ra_cdf  ) )DEALLOCATE(td_rnge%ra_cdf  )
    ALLOCATE( td_rnge%ra_rands(td_rnge%i_lb:td_rnge%i_ub),&
              td_rnge%ra_cdf  (td_rnge%i_lb:td_rnge%i_ub) &
    )
    td_rnge%ra_rands = 0.0_cp
    td_rnge%ra_cdf   = 0.0_cp
    !stats
    IF(td_rnge%l_stat)THEN
      IF( ALLOCATED(td_rnge%ra_pdf) )DEALLOCATE(td_rnge%ra_pdf)
      IF( ALLOCATED(td_rnge%ia_stats) )DEALLOCATE(td_rnge%ia_stats)
      ALLOCATE(td_rnge%ra_pdf(td_rnge%i_lb:td_rnge%i_ub),&
              td_rnge%ia_stats(td_rnge%i_lb:td_rnge%i_ub) &
      )
      td_rnge%i_nCall  = 0
      td_rnge%ia_stats = 0
      td_rnge%ra_pdf   = 0.0_cp
    END IF
    td_rnge%l_init = .TRUE.
  END SUBROUTINE init_rng_environment

  !> \brief finalizes the environment for a random number generator
  !! \param[in,out] td_rnge environment to be initialized
  !<
  SUBROUTINE finilize_rng_environment(td_rnge)
    TYPE(rng_environment), INTENT(INOUT) :: td_rnge

    !statistics
    IF(td_rnge%l_init)THEN
      IF(td_rnge%l_stat)THEN
        DEALLOCATE(td_rnge%ra_pdf, td_rnge%ia_stats )
      END IF
      DEALLOCATE( td_rnge%ra_rands, td_rnge%ra_cdf )
      td_rnge%i_lb =  0
      td_rnge%i_ub = -1! an upper bound less than the lower bound
      td_rnge%i_nCall  = -1
      td_rnge%l_init = .FALSE.
    END IF
  END SUBROUTINE finilize_rng_environment

  !> \brief generates a random number from the distribution associated with the random number generator environment
  !! \param[in,out] td_rnge environment of the random number generator to be used
  !<
  FUNCTION generate_rand(td_rnge) RESULT(rl_rand)
    TYPE(rng_environment), INTENT(INOUT) :: td_rnge
    REAL(KIND=cp) :: rl_cdf, rl_rand
    INTEGER :: il_pos, il_ntry

    IF(.NOT.td_rnge%l_init)THEN
      CALL debug("In generate_rand: random number generator is not initialized", tag=dALLWAYS)
      CALL ABORT()
    END IF

    CALL RANDOM_NUMBER(rl_cdf)
    il_pos = search_pos_inc(rl_cdf, td_rnge%ra_cdf, il_ntry) + td_rnge%i_lb - 1
    !WRITE(*,FMT="(A,ES13.5E2,A,I5,A,I5,A)")"Find ", rl_cdf, " At ", il_pos, " with ", il_ntry, " tries"
    rl_rand = td_rnge%ra_rands(il_pos)
    IF(td_rnge%l_stat)THEN
      td_rnge%i_nCall = td_rnge%i_nCall + 1
      td_rnge%ia_stats(il_pos) = td_rnge%ia_stats(il_pos) + 1!for stats
    END IF
  END FUNCTION generate_rand

!   !> \brief updates the statistics on a random number generator using the cfd
!   !! \param[in,out] td_rnge structure that keeps stats
!   !! \param[in] index of the position to be updated
!   !! this function is used to update statistics for external random number generator
!   !<
!   SUBROUTINE update_rnge_stat(td_rnge, pos)
!     TYPE(rng_environment), INTENT(INOUT) :: td_rnge
!     INTEGER, INTENT(IN) :: pos
!     !local variables
!     INTEGER :: il_ntry
!
!     il_pos = search_pos_inc(rd_rand, td_rnge%ra_rands, il_ntry) + td_rnge%i_lb - 1
!     IF(td_rnge%l_stat)THEN
!       td_rnge%i_nCall = td_rnge%i_nCall + 1
!       td_rnge%ia_stats(il_pos) = td_rnge%ia_stats(il_pos) + 1!for stats
!     END IF
!   END SUBROUTINE update_rnge_stat

  !> returns the statistics of the random number generator
  !< \param[in] td_rnge, environment of the random number generator to be used
  !! \param[out] ssize, size of the statistic vector
  !! \param[out] x vector of abcissa of the statistic points
  !! \param[out] y vector of estimated pdf based on calls to ramdom_normal since the last call to init_normal_rand
  !! \param[out] pdf vector of true pdf
  !! \param[out] stat call stat, return 0 if OK, -1 if statistics are not saved (call to init_normal_rand with stat = .FALSE., default)
  !! \detail this routine return usefull information to check the gaussianity of the seauence of number generated since the last call to init_normal_rand
  !! \this routine should be called only if init_normal_rand has been called with stat = .TRUE.
  !<
  SUBROUTINE get_rng_stat(td_rnge, ssize, x, y, pdf, stat)
    TYPE(rng_environment), INTENT(IN) :: td_rnge
    INTEGER, INTENT(OUT), OPTIONAL :: ssize, stat
    REAL(KIND=cp), DIMENSION(:), INTENT(OUT), OPTIONAL :: x, y, pdf
    !local var
    INTEGER :: il_tmp

    IF( PRESENT(ssize) ) ssize = SIZE(td_rnge%ia_stats)
    IF(td_rnge%l_stat)THEN
      IF( PRESENT(x) ) x = td_rnge%ra_rands
      IF( PRESENT(y) )THEN
        !the count of values in a small interval is assumed to be the aproximation (rectangle methods) of the probability of a random values to be in this interval,
        il_tmp = td_rnge%i_lb
        y = ( REAL(td_rnge%ia_stats, cp)/REAL(td_rnge%i_nCall,cp) )/( td_rnge%ra_rands(il_tmp+1)-td_rnge%ra_rands(il_tmp) )
      END IF
      IF( PRESENT(pdf) ) pdf = td_rnge%ra_pdf
      IF( PRESENT(stat) ) stat = 0
    ELSE
      IF( PRESENT(stat) ) stat = -1
    END IF
  END SUBROUTINE get_rng_stat

!------------------
  !> \brief Generates a gaussian random number of preset standard deviation and mean
  !! initially named normal_rand_scalar_func and defined in the interface normal_rand
  !! Since the other porcedure of the interface normal_rand are subroutines, it did not respect the standard, although working with intel compilers
  !<
  FUNCTION normal_rand() RESULT(rl_rand)
    !local var
    REAL(KIND=cp) :: rl_rand
    !REAL(KIND=cp) :: rl_sign, rl_cdf
    !INTEGER :: il_ntry, il_pos

    rl_rand = generate_rand(tm_gaussp%t_env)
    !il_pos = search_pos_inc(rl_cdf, tm_gaussp%t_env%ra_cdf, il_ntry) + tm_gaussp%t_env%i_lb - 1
    !rl_rand = tm_gaussp%t_env%ra_rands(il_pos)
  END FUNCTION normal_rand

  !> \brief Generates a gaussian random number of preset standard deviation and mean
  !!
  !<
  SUBROUTINE normal_random_scalar(rd_rand)
    REAL(KIND=cp), INTENT(OUT) :: rd_rand
    !REAL(KIND=cp) :: rl_sign, rl_cdf
    !INTEGER :: il_ntry, il_pos

    rd_rand = generate_rand(tm_gaussp%t_env)
    !il_pos = search_pos_inc(rl_cdf, tm_gaussp%t_env%ra_cdf, il_ntry) + tm_gaussp%t_env%i_lb - 1
    !rd_rand = tm_gaussp%t_env%ra_rands(il_pos)
  END SUBROUTINE normal_random_scalar

  !> \brief Generates a vector of independant gaussian random numbers of preset standard deviation and mean
  !!
  !<
  SUBROUTINE normal_random_vector(rda_rand)
    REAL(KIND=cp), DIMENSION(:), INTENT(OUT) :: rda_rand
    !REAL(KIND=cp) :: rl_sign, rl_cdf
    INTEGER :: ibi!, il_ntry, il_pos

    DO ibi = 1, SIZE(rda_rand)
      CALL init_random_seed()
      rda_rand(ibi) = generate_rand(tm_gaussp%t_env)
      !il_pos = search_pos_inc(rl_cdf, tm_gaussp%t_env%ra_cdf, il_ntry) + tm_gaussp%t_env%i_lb - 1
      !rd_rand = tm_gaussp%t_env%ra_rands(il_pos)
    END DO
  END SUBROUTINE normal_random_vector

  !> \brief Initializes the Gaussian generator
  !! \param[in] mu mean of the distribution
  !! \param[in] sigma Standard deviation of the distribution
  !! \param[in] radius Distance from the mean to the maximum/minimum number that can be generated, default is set to 4*rd_sigma
  !! \param[in] nright Number of possible values greater than the mean, default is set to 127
  !! \param[in] stat says if it is necessary to save statistics
  !! \details the total number of possible values that can be generated is 2*id_nright + 1
  !<
  SUBROUTINE init_normal_rand(mu, sigma, radius, nright, stat)
    REAL(KIND=cp), OPTIONAL, INTENT(IN) :: mu, sigma
    REAL(KIND=cp), OPTIONAL, INTENT(IN) :: radius
    INTEGER, OPTIONAL, INTENT(IN) :: nright
    LOGICAL, OPTIONAL, INTENT(IN) :: stat
    REAL(KIND=cp) :: rl_hstep
    INTEGER :: ibi, il_lb, il_ub

    IF ( PRESENT(mu) ) THEN
      tm_gaussp%r_mean  = mu
    ELSE
      tm_gaussp%r_mean = 0.0_cp
    END IF
    IF ( PRESENT(sigma) ) THEN
      tm_gaussp%r_sigma = sigma
    ELSE
      tm_gaussp%r_sigma = 1.0_cp
    END IF
    IF( PRESENT(radius) ) THEN
      tm_gaussp%r_radius = radius
    ELSE
      tm_gaussp%r_radius = 4.0_cp*tm_gaussp%r_sigma
    END IF
    IF( PRESENT(nright) ) THEN
      tm_gaussp%i_nright = nright
    ELSE
      tm_gaussp%i_nright = DEFAULT_NRIGHT
    END IF
    il_lb = -tm_gaussp%i_nright
    il_ub =  tm_gaussp%i_nright-1

    CALL init_rng_environment(tm_gaussp%t_env, il_lb, il_ub, stat)

    tm_gaussp%r_step = tm_gaussp%r_radius/( REAL(tm_gaussp%i_nright, cp)-0.5_cp)
    rl_hstep= tm_gaussp%r_step/2.0_cp
    DO ibi = il_lb, il_ub
      tm_gaussp%t_env%ra_rands(ibi) = (ibi)*tm_gaussp%r_step + tm_gaussp%r_mean
      tm_gaussp%t_env%ra_cdf(ibi)   = normal_cdf( tm_gaussp%t_env%ra_rands(ibi)-rl_hstep, tm_gaussp%r_mean, tm_gaussp%r_sigma)
      !stats
      IF(tm_gaussp%t_env%l_stat)THEN
        tm_gaussp%t_env%ia_stats(ibi) = 0
        tm_gaussp%t_env%ra_pdf(ibi) = &
          normal_pdf( tm_gaussp%t_env%ra_rands(ibi), tm_gaussp%r_mean, tm_gaussp%r_sigma )
      END IF
      !CALL debug(ibi, 'init_normal_rand loop..', tag=dALLWAYS)
    END DO
    CALL init_random_seed()
  END SUBROUTINE init_normal_rand

  SUBROUTINE copy_normal_rnge(td_rnge)
    TYPE(rng_environment), INTENT(INOUT) :: td_rnge

    CALL finilize_rng_environment(td_rnge)
    td_rnge%l_init = tm_gaussp%t_env%l_init
    IF(tm_gaussp%t_env%l_init)THEN
      CALL init_rng_environment(td_rnge, tm_gaussp%t_env%i_lb, tm_gaussp%t_env%i_ub, tm_gaussp%t_env%l_stat)
      td_rnge%ra_rands = tm_gaussp%t_env%ra_rands
      td_rnge%ra_cdf   = tm_gaussp%t_env%ra_cdf
      IF (td_rnge%l_stat) THEN
        td_rnge%ra_pdf   = tm_gaussp%t_env%ra_pdf
        td_rnge%ia_stats = tm_gaussp%t_env%ia_stats
        td_rnge%i_nCall  = tm_gaussp%t_env%i_nCall
      END IF
    END IF
  END SUBROUTINE copy_normal_rnge

  !> returns statistics of the random module
  !! \param[out] ssize size of the statistic vector
  !! \param[out] x vector of abcissa of the statistic points
  !! \param[out] y vector of estimated pdf based on calls to ramdom_normal since the last call to init_normal_rand
  !! \param[out] pdf vector of true pdf
  !! \param[out] stat call stat, return 0 if OK, -1 if statistics are not saved (call to init_normal_rand with stat = .FALSE., default)
  !! \details this routine return usefull information to check the gaussianity of the seauence of number generated since the last call to \a init_normal_rand.
  !! This routine should be called only if \a init_normal_rand has been called with stat = .TRUE.
  !<
  SUBROUTINE get_normal_stat(ssize, x, y, pdf, stat)
    INTEGER, INTENT(OUT), OPTIONAL :: ssize, stat
    REAL(KIND=cp), DIMENSION(:), INTENT(OUT), OPTIONAL :: x, y, pdf
    !INTEGER :: ibi

    CALL get_rng_stat(tm_gaussp%t_env, ssize, x, y, pdf, stat)
  END SUBROUTINE get_normal_stat

  SUBROUTINE finilize_normal_rand()
    !TYPE(dyn_rVector), DIMENSION(3) :: tla_save
    IF(tm_gaussp%i_nright>0)THEN
      tm_gaussp%i_nright = 0
      CALL finilize_rng_environment(tm_gaussp%t_env)
    END IF
  END SUBROUTINE finilize_normal_rand

  !> \brief Computes the normal probability density function at point x
  !! \param[in] rd_x point where the PDF is calculated
  !! \param[in] mu (optional, default 0) mean of the distribution
  !! \param[in] sigma (optional, default 1) standard deviation of the distribution
  !<
  FUNCTION normal_pdf(rd_x, mu, sigma) RESULT(rl_pdf)
    REAL(KIND=cp), INTENT(IN) :: rd_x
    REAL(KIND=cp), INTENT(IN), OPTIONAL :: mu, sigma
    REAL(KIND=cp) :: rl_pdf, rl_mu, rl_sigma

    IF ( PRESENT(mu) ) THEN
      rl_mu = mu
    ELSE
      rl_mu = 0.0
    END IF
    IF ( PRESENT(sigma) ) THEN
      rl_sigma = sigma
    ELSE
      rl_sigma = 1.0
    END IF

    rl_pdf = ( 1/( rl_sigma*SQRT(2*rp_pi) ) )*exp(-(rd_x-rl_mu)**2/(2.0*rl_sigma**2) )
  END FUNCTION normal_pdf

  !> \brief Computes the normal Cumulative distribution function at point x
  !! \param[in] rd_x point where the CDF is calculated
  !! \param[in] mu (optional, default 0) mean of the distribution
  !! \param[in] sigma (optional, default 1) standard deviation of the distribution
  !<
  FUNCTION normal_cdf(rd_x, mu, sigma) RESULT(rl_cdf)
    REAL(KIND=cp), INTENT(IN) :: rd_x
    REAL(KIND=cp), INTENT(IN), OPTIONAL :: mu, sigma
    REAL(KIND=cp) :: rl_cdf, rl_mu, rl_sigma

    IF ( PRESENT(mu) ) THEN
      rl_mu = mu
    ELSE
      rl_mu = 0.0
    END IF
    IF ( PRESENT(sigma) ) THEN
      rl_sigma = sigma
    ELSE
      rl_sigma = 1.0
    END IF

    rl_cdf = 0.5*( 1 + erf( (rd_x-rl_mu)/(rl_sigma*SQRT(2.0)) ) )
  END FUNCTION normal_cdf

END MODULE randmod


text:
rcc + tab

react component skeleton (rcc,tab)


text:
let ace = require('ace');
let ace_mode_json = require('ace/mode-json');
let x_json = require('./x_json_highlight_rules');

var oop = ace.require("ace/lib/oop");
var JsonHighlightRules = ace.require("ace/mode/json_highlight_rules").JsonHighlightRules;

var OutputJsonHighlightRules = function () {

  this.$rules = {};

  x_json.addToRules(this, 'start');

  this.$rules.start.unshift(
    {
      "token": "comment",
      "regex": "#.*$"
    }
  );

  if (this.constructor === OutputJsonHighlightRules) {
    this.normalizeRules();
  }

};

oop.inherits(OutputJsonHighlightRules, JsonHighlightRules);

module.exports.OutputJsonHighlightRules = OutputJsonHighlightRules;


text:
import Ember from 'ember';

export function numberWithSuffix(inputString) {
	var input = parseInt(inputString, 10);
	Ember.assert("Missing required maxValue argument", input === 0 || !Ember.isEmpty(input));
	var result = '';
	if (input > 1000 && input < 1000000) {
		result = '%@K'.fmt(Math.round(input/1000));
	}
	else if (input > 1000000 && input < 1000000000) {
		result = '%@M'.fmt(Math.round(input/1000000));
	}
	else if (input > 1000000000) {
		result = '%@B'.fmt(Math.round(input/1000000000));
	}
	else {
		result = input;
	}

  return new Ember.Handlebars.SafeString(result);
}

export default Ember.Handlebars.makeBoundHelper(numberWithSuffix);


text:
void GameMenu::Init()
{
	InitGraphics();
	InitializeButtons();

	//	Testing texts
	txt1 = graphics.RequestText({ 200, 500 }, "SAMPLE TEXT1");
	txt2 = graphics.RequestText({ 500, 200 }, "SAMPLE TEXT2");
	txt3 = graphics.RequestText({ 500, 500 }, "SAMPLE TEXT3");

#if ANIM_BOX_DEBUG
	anim.animationComponent.RequestTileset(2, 64, 64, 6);
	anim.animationComponent.graphicComponent.SetOrigin({ 32, 32 });
	anim.animationComponent.AddAnimation(0, 2, 500);
	anim.animationComponent.AddAnimation(3, 5, 500);
#endif
}

void GameMenu::Update(const sf::Time& timeStep)
{
	UpdateCloudAnimation();
	for (int i = 0; i < BUTTONS_NUMBER; ++i)
		buttons[i].Update();
#if ANIM_BOX_DEBUG
	anim.Update(timeStep);
#endif
}

void GameMenu::SetInput(const sf::Event ev)
{
#if ANIM_BOX_DEBUG
	if (ev.type == sf::Event::EventType::KeyPressed)
	{
		anim.animationComponent.Play(0);
		anim.physicsComponent.SetTarget(sf::Vector2f(32, 32), anim.velocity);
	}
	else if (ev.type == sf::Event::EventType::MouseButtonPressed)
	{
		anim.animationComponent.Play(1);
		anim.physicsComponent.SetTarget(sf::Vector2f((float)ev.mouseButton.x, (float)ev.mouseButton.y), anim.velocity);
	}
#endif
	enterShop.SetInput(ev);
	enterBattle.SetInput(ev);
	goBack.SetInput(ev);
}

inline void GameMenu::InitializeButtons()
{
	for (int i = 0; i < BUTTONS_NUMBER; ++i)
		buttons[i].Init();

	constexpr float bottomPadding = BUTTON_HEIGHT + 50;
	constexpr float rightPadding = BUTTON_WIDTH + 60;
	constexpr float horizontalPosition = WINDOW_WIDTH - rightPadding;
	constexpr float baseVerticalPosition = WINDOW_HEIGHT - bottomPadding;

	for (int i = 0; i < BUTTONS_NUMBER; ++i)
	{
		const float verticalPosition = baseVerticalPosition - (BUTTONS_NUMBER - 1 - i) * BUTTON_HEIGHT;
		buttons[i].SetPosition({ horizontalPosition, verticalPosition });
	}
}

inline void GameMenu::InitGraphics()
{
	clouds_sid = graphics.RequestSprite(GraphicEngine::GraphicID::MenuBackgroundClouds, 0);
	uint8 sid = graphics.RequestSprite(GraphicEngine::GraphicID::GameMenuBackground, 0);
	graphics.SetPosition(sid, { 0, 0 });
}

inline void GameMenu::UpdateCloudAnimation()
{
	//reset position when it's half-way through
	if (graphics.GetPosition(clouds_sid).y < -WINDOW_HEIGHT)
		graphics.SetPosition(clouds_sid, { 0, 0 });
	graphics.MoveBy(clouds_sid, { 0, -1 });
}


text:
using Castle.Windsor;
using Castle.Windsor.Installer;
using Code.Service;
using System.Web.Http;

namespace Trainer.Web
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            var container = new WindsorContainer();
            container.Install(FromAssembly.This());
            config.DependencyResolver = new WindsorDependencyResolver(container.Kernel);

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Routes.MapHttpRoute(
                name: "AddApi",
                routeTemplate: "api/{controller}/add"
            );

            config.Routes.MapHttpRoute(
                name: "AddCompleteApi",
                routeTemplate: "api/{controller}/add/complete"
            );

            config.Routes.MapHttpRoute(
                name: "RemoveApi",
                routeTemplate: "api/{controller}/remove/{id}",
                defaults: new { id = string.Empty }
            );

            config.Routes.MapHttpRoute(
                name: "RemoveGenericApi",
                routeTemplate: "api/{controller}/remove/{propertyName}/{id}",
                defaults: new { propertyName = string.Empty, id = string.Empty }
            );

            config.Routes.MapHttpRoute(
                name: "UpdateGenericApi",
                routeTemplate: "api/{controller}/update/{id}",
                defaults: new { id = string.Empty }
            );

            config.Routes.MapHttpRoute(
                name: "AuthenticateApi",
                routeTemplate: "api/{controller}/authenticate"
            );

            config.Routes.MapHttpRoute(
                name: "TokenAuthenticateApi",
                routeTemplate: "api/{controller}/token/{code}",
                defaults: new { code = string.Empty }
            );

            container.Resolve<CodeServiceWeb>().LoadPresentations();
        }
    }
}


text:
package net.ossrs.yasea;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;

import com.coremedia.iso.BoxParser;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.IsoTypeWriter;
import com.coremedia.iso.boxes.AbstractMediaHeaderBox;
import com.coremedia.iso.boxes.Box;
import com.coremedia.iso.boxes.ContainerBox;
import com.coremedia.iso.boxes.DataEntryUrlBox;
import com.coremedia.iso.boxes.DataInformationBox;
import com.coremedia.iso.boxes.DataReferenceBox;
import com.coremedia.iso.boxes.FileTypeBox;
import com.coremedia.iso.boxes.HandlerBox;
import com.coremedia.iso.boxes.MediaBox;
import com.coremedia.iso.boxes.MediaHeaderBox;
import com.coremedia.iso.boxes.MediaInformationBox;
import com.coremedia.iso.boxes.MovieBox;
import com.coremedia.iso.boxes.MovieHeaderBox;
import com.coremedia.iso.boxes.SampleDescriptionBox;
import com.coremedia.iso.boxes.SampleSizeBox;
import com.coremedia.iso.boxes.SampleTableBox;
import com.coremedia.iso.boxes.SampleToChunkBox;
import com.coremedia.iso.boxes.SoundMediaHeaderBox;
import com.coremedia.iso.boxes.StaticChunkOffsetBox;
import com.coremedia.iso.boxes.SyncSampleBox;
import com.coremedia.iso.boxes.TimeToSampleBox;
import com.coremedia.iso.boxes.TrackBox;
import com.coremedia.iso.boxes.TrackHeaderBox;
import com.coremedia.iso.boxes.VideoMediaHeaderBox;
import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.AudioSpecificConfig;
import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.DecoderConfigDescriptor;
import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.SLConfigDescriptor;
import com.googlecode.mp4parser.util.Math;
import com.googlecode.mp4parser.util.Matrix;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Created by LeoMa on 2016/5/21.
 */
public class SrsMp4Muxer {

    private static final String TAG = "SrsMp4Muxer";
    private static final int VIDEO_TRACK = 100;
    private static final int AUDIO_TRACK = 101;

    private File mRecFile;
    private SrsRecordHandler mHandler;

    private MediaFormat videoFormat = null;
    private MediaFormat audioFormat = null;

    private SrsRawH264Stream avc = new SrsRawH264Stream();
    private Mp4Movie mp4Movie = new Mp4Movie();

    private boolean aacSpecConfig = false;
    private ByteBuffer h264_sps = null;
    private ByteBuffer h264_pps = null;
    private ArrayList<byte[]> spsList = new ArrayList<>();
    private ArrayList<byte[]> ppsList = new ArrayList<>();

    private Thread worker;
    private volatile boolean bRecording = false;
    private volatile boolean bPaused = false;
    private volatile boolean needToFindKeyFrame = true;
    private final Object writeLock = new Object();
    private ConcurrentLinkedQueue<SrsEsFrame> frameCache = new ConcurrentLinkedQueue<>();

    private static Map<Integer, Integer> samplingFrequencyIndexMap = new HashMap<>();

    static {
        samplingFrequencyIndexMap.put(96000, 0x0);
        samplingFrequencyIndexMap.put(88200, 0x1);
        samplingFrequencyIndexMap.put(64000, 0x2);
        samplingFrequencyIndexMap.put(48000, 0x3);
        samplingFrequencyIndexMap.put(44100, 0x4);
        samplingFrequencyIndexMap.put(32000, 0x5);
        samplingFrequencyIndexMap.put(24000, 0x6);
        samplingFrequencyIndexMap.put(22050, 0x7);
        samplingFrequencyIndexMap.put(16000, 0x8);
        samplingFrequencyIndexMap.put(12000, 0x9);
        samplingFrequencyIndexMap.put(11025, 0xa);
        samplingFrequencyIndexMap.put(8000, 0xb);
    }

    public SrsMp4Muxer(SrsRecordHandler handler) {
        mHandler = handler;
    }

    /**
     * start recording.
     */
    public boolean record(File outputFile) {
        if (videoFormat == null && audioFormat == null) {
            return false;
        }

        mRecFile = outputFile;
        createMovie(mRecFile);
        mHandler.notifyRecordStarted(mRecFile.getPath());

        if (!spsList.isEmpty() && !ppsList.isEmpty()) {
            mp4Movie.addTrack(videoFormat, false);
        }
        mp4Movie.addTrack(audioFormat, true);

        worker = new Thread(new Runnable() {
            @Override
            public void run() {
                bRecording = true;
                while (bRecording) {
                    // Keep at least one audio and video frame in cache to ensure monotonically increasing.
                    while (!frameCache.isEmpty()) {
                        SrsEsFrame frame = frameCache.poll();
                        writeSampleData(frame.bb, frame.bi, frame.is_audio());
                    }
                    // Waiting for next frame
                    synchronized (writeLock) {
                        try {
                            // isEmpty() may take some time, so we set timeout to detect next frame
                            writeLock.wait(500);
                        } catch (InterruptedException ie) {
                            worker.interrupt();
                        }
                    }
                }
            }
        });
        worker.start();

        return true;
    }

    /**
     * pause recording.
     */
    public void pause() {
        if (bRecording) {
            bPaused = true;
            mHandler.notifyRecordPause();
        }
    }

    /**
     * resume recording.
     */
    public void resume() {
        if (bRecording) {
            bPaused = false;
            needToFindKeyFrame = true;
            mHandler.notifyRecordResume();
        }
    }

    /**
     * finish recording.
     */
    public void stop() {
        bRecording = false;
        bPaused = false;
        needToFindKeyFrame = true;
        aacSpecConfig = false;
        frameCache.clear();

        if (worker != null) {
            try {
                worker.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
                worker.interrupt();
            }
            worker = null;

            finishMovie();
            mHandler.notifyRecordFinished(mRecFile.getPath());
        }
        Log.i(TAG, "SrsMp4Muxer closed");
    }

    /**
     * Adds a track with the specified format.
     *
     * @param format The media format for the track.
     * @return The track index for this newly added track.
     */
    public int addTrack(MediaFormat format) {
        if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
            videoFormat = format;
            return VIDEO_TRACK;
        } else {
            audioFormat = format;
            return AUDIO_TRACK;
        }
    }

    /**
     * send the annexb frame to SRS over RTMP.
     *
     * @param trackIndex The track index for this sample.
     * @param byteBuf    The encoded sample.
     * @param bufferInfo The buffer information related to this sample.
     */
    public void writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo) {
        if (VIDEO_TRACK == trackIndex) {
            writeVideoSample(byteBuf, bufferInfo);
        } else {
            writeAudioSample(byteBuf, bufferInfo);
        }
    }

    /**
     * Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes
     * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
     */
    private class SrsAvcNaluType
    {
        // Unspecified
        public final static int Reserved = 0;

        // Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
        public final static int NonIDR = 1;
        // Coded slice data partition A slice_data_partition_a_layer_rbsp( )
        public final static int DataPartitionA = 2;
        // Coded slice data partition B slice_data_partition_b_layer_rbsp( )
        public final static int DataPartitionB = 3;
        // Coded slice data partition C slice_data_partition_c_layer_rbsp( )
        public final static int DataPartitionC = 4;
        // Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
        public final static int IDR = 5;
        // Supplemental enhancement information (SEI) sei_rbsp( )
        public final static int SEI = 6;
        // Sequence parameter set seq_parameter_set_rbsp( )
        public final static int SPS = 7;
        // Picture parameter set pic_parameter_set_rbsp( )
        public final static int PPS = 8;
        // Access unit delimiter access_unit_delimiter_rbsp( )
        public final static int AccessUnitDelimiter = 9;
        // End of sequence end_of_seq_rbsp( )
        public final static int EOSequence = 10;
        // End of stream end_of_stream_rbsp( )
        public final static int EOStream = 11;
        // Filler data filler_data_rbsp( )
        public final static int FilterData = 12;
        // Sequence parameter set extension seq_parameter_set_extension_rbsp( )
        public final static int SPSExt = 13;
        // Prefix NAL unit prefix_nal_unit_rbsp( )
        public final static int PrefixNALU = 14;
        // Subset sequence parameter set subset_seq_parameter_set_rbsp( )
        public final static int SubsetSPS = 15;
        // Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
        public final static int LayerWithoutPartition = 19;
        // Coded slice extension slice_layer_extension_rbsp( )
        public final static int CodedSliceExt = 20;
    }

    private void writeVideoSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
        int nal_unit_type = bb.get(4) & 0x1f;
        if (nal_unit_type == SrsAvcNaluType.IDR || nal_unit_type == SrsAvcNaluType.NonIDR) {
            writeFrameByte(VIDEO_TRACK, bb, bi, nal_unit_type == SrsAvcNaluType.IDR);
        } else {
            while (bb.position() < bi.size) {
                SrsEsFrameBytes frame = avc.annexb_demux(bb, bi);

                if (avc.is_sps(frame)) {
                    if (!frame.data.equals(h264_sps)) {
                        byte[] sps = new byte[frame.size];
                        frame.data.get(sps);
                        h264_sps = ByteBuffer.wrap(sps);
                        spsList.clear();
                        spsList.add(sps);
                    }
                    continue;
                }

                if (avc.is_pps(frame)) {
                    if (!frame.data.equals(h264_pps)) {
                        byte[] pps = new byte[frame.size];
                        frame.data.get(pps);
                        h264_pps = ByteBuffer.wrap(pps);
                        ppsList.clear();
                        ppsList.add(pps);
                    }
                    continue;
                }
            }
        }
    }

    private void writeAudioSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
        if (!aacSpecConfig) {
            aacSpecConfig = true;
        } else {
            writeFrameByte(AUDIO_TRACK, bb, bi, false);
        }
    }

    private void writeFrameByte(int track, ByteBuffer bb, MediaCodec.BufferInfo bi, boolean isKeyFrame) {
        SrsEsFrame frame = new SrsEsFrame();
        frame.bb = bb;
        frame.bi = bi;
        frame.isKeyFrame = isKeyFrame;
        frame.track = track;

        if (bRecording && !bPaused) {
            if (needToFindKeyFrame) {
                if (frame.isKeyFrame) {
                    needToFindKeyFrame = false;
                    frameCache.add(frame);
                    synchronized (writeLock) {
                        writeLock.notifyAll();
                    }
                }
            } else {
                frameCache.add(frame);
                synchronized (writeLock) {
                    writeLock.notifyAll();
                }
            }
        }
    }

    /**
     * the search result for annexb.
     */
    private class SrsAnnexbSearch {
        public int nb_start_code = 0;
        public boolean match = false;
    }

    /**
     * the demuxed tag frame.
     */
    private class SrsEsFrameBytes {
        public ByteBuffer data;
        public int size;
    }

    /**
     * the AV frame.
     */
    private class SrsEsFrame {
        public ByteBuffer bb;
        public MediaCodec.BufferInfo bi;
        public int track;
        public boolean isKeyFrame;

        public boolean is_video() {
            return track == VIDEO_TRACK;
        }

        public boolean is_audio() {
            return track == AUDIO_TRACK;
        }
    }

    /**
     * the raw h.264 stream, in annexb.
     */
    private class SrsRawH264Stream {
        public boolean is_sps(SrsEsFrameBytes frame) {
            if (frame.size < 1) {
                return false;
            }

            return (frame.data.get(0) & 0x1f) == SrsAvcNaluType.SPS;
        }

        public boolean is_pps(SrsEsFrameBytes frame) {
            if (frame.size < 1) {
                return false;
            }
            return (frame.data.get(0) & 0x1f) == SrsAvcNaluType.PPS;
        }

        public SrsAnnexbSearch srs_avc_startswith_annexb(ByteBuffer bb, MediaCodec.BufferInfo bi) {
            SrsAnnexbSearch as = new SrsAnnexbSearch();
            as.match = false;

            int pos = bb.position();
            while (pos < bi.size - 3) {
                // not match.
                if (bb.get(pos) != 0x00 || bb.get(pos + 1) != 0x00) {
                    break;
                }

                // match N[00] 00 00 01, where N>=0
                if (bb.get(pos + 2) == 0x01) {
                    as.match = true;
                    as.nb_start_code = pos + 3 - bb.position();
                    break;
                }

                pos++;
            }

            return as;
        }

        public SrsEsFrameBytes annexb_demux(ByteBuffer bb, MediaCodec.BufferInfo bi) {
            SrsEsFrameBytes tbb = new SrsEsFrameBytes();

            while (bb.position() < bi.size) {
                // each frame must prefixed by annexb format.
                // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
                SrsAnnexbSearch tbbsc = srs_avc_startswith_annexb(bb, bi);
                if (!tbbsc.match || tbbsc.nb_start_code < 3) {
                    Log.e(TAG, "annexb not match.");
                    mHandler.notifyRecordIllegalArgumentException(new IllegalArgumentException(
                        String.format("annexb not match for %dB, pos=%d", bi.size, bb.position())));
                }

                // the start codes.
                ByteBuffer tbbs = bb.slice();
                for (int i = 0; i < tbbsc.nb_start_code; i++) {
                    bb.get();
                }

                // find out the frame size.
                tbb.data = bb.slice();
                int pos = bb.position();
                while (bb.position() < bi.size) {
                    SrsAnnexbSearch bsc = srs_avc_startswith_annexb(bb, bi);
                    if (bsc.match) {
                        break;
                    }
                    bb.get();
                }

                tbb.size = bb.position() - pos;
                break;
            }

            return tbb;
        }
    }

    private class Sample {
        private long offset = 0;
        private long size = 0;

        public Sample(long offset, long size) {
            this.offset = offset;
            this.size = size;
        }

        public long getOffset() {
            return offset;
        }

        public long getSize() {
            return size;
        }
    }

    private class Track {
        private int trackId = 0;
        private ArrayList<Sample> samples = new ArrayList<>();
        private long duration = 0;
        private String handler;
        private AbstractMediaHeaderBox headerBox = null;
        private SampleDescriptionBox sampleDescriptionBox = null;
        private LinkedList<Integer> syncSamples = null;
        private int timeScale;
        private Date creationTime = new Date();
        private int height;
        private int width;
        private float volume = 0;
        private ArrayList<Long> sampleDurations = new ArrayList<>();
        private boolean isAudio = false;
        private long lastPresentationTimeUs = 0;
        private boolean first = true;

        public Track(int id, MediaFormat format, boolean audio) {
            trackId = id;
            isAudio = audio;
            if (!isAudio) {
                sampleDurations.add((long) 3015);
                duration = 3015;
                width = format.getInteger(MediaFormat.KEY_WIDTH);
                height = format.getInteger(MediaFormat.KEY_HEIGHT);
                timeScale = 90000;
                syncSamples = new LinkedList<>();
                handler = "vide";
                headerBox = new VideoMediaHeaderBox();
                sampleDescriptionBox = new SampleDescriptionBox();
                if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
                    VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1");
                    visualSampleEntry.setDataReferenceIndex(1);
                    visualSampleEntry.setDepth(24);
                    visualSampleEntry.setFrameCount(1);
                    visualSampleEntry.setHorizresolution(72);
                    visualSampleEntry.setVertresolution(72);
                    visualSampleEntry.setWidth(width);
                    visualSampleEntry.setHeight(height);
                    visualSampleEntry.setCompressorname("AVC Coding");

                    AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox();
                    avcConfigurationBox.setConfigurationVersion(1);
                    avcConfigurationBox.setAvcProfileIndication((int) h264_sps.get(1));
                    avcConfigurationBox.setProfileCompatibility(0);
                    avcConfigurationBox.setAvcLevelIndication((int) h264_sps.get(3));
                    avcConfigurationBox.setLengthSizeMinusOne(3);
                    avcConfigurationBox.setSequenceParameterSets(spsList);
                    avcConfigurationBox.setPictureParameterSets(ppsList);
                    avcConfigurationBox.setBitDepthLumaMinus8(-1);
                    avcConfigurationBox.setBitDepthChromaMinus8(-1);
                    avcConfigurationBox.setChromaFormat(-1);
                    avcConfigurationBox.setHasExts(false);

                    visualSampleEntry.addBox(avcConfigurationBox);
                    sampleDescriptionBox.addBox(visualSampleEntry);
                }
            } else {
                sampleDurations.add((long) 1024);
                duration = 1024;
                volume = 1;
                timeScale = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                handler = "soun";
                headerBox = new SoundMediaHeaderBox();
                sampleDescriptionBox = new SampleDescriptionBox();
                AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a");
                audioSampleEntry.setChannelCount(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
                audioSampleEntry.setSampleRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
                audioSampleEntry.setDataReferenceIndex(1);
                audioSampleEntry.setSampleSize(16);

                ESDescriptorBox esds = new ESDescriptorBox();
                ESDescriptor descriptor = new ESDescriptor();
                descriptor.setEsId(0);

                SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor();
                slConfigDescriptor.setPredefined(2);
                descriptor.setSlConfigDescriptor(slConfigDescriptor);

                DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor();
                decoderConfigDescriptor.setObjectTypeIndication(0x40);
                decoderConfigDescriptor.setStreamType(5);
                decoderConfigDescriptor.setBufferSizeDB(1536);
                decoderConfigDescriptor.setMaxBitRate(96000);
                decoderConfigDescriptor.setAvgBitRate(96000);

                AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
                audioSpecificConfig.setAudioObjectType(2);
                audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int) audioSampleEntry.getSampleRate()));
                audioSpecificConfig.setChannelConfiguration(audioSampleEntry.getChannelCount());
                decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);

                descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor);

                ByteBuffer data = descriptor.serialize();
                esds.setEsDescriptor(descriptor);
                esds.setData(data);
                audioSampleEntry.addBox(esds);
                sampleDescriptionBox.addBox(audioSampleEntry);
            }
        }

        public void addSample(long offset, MediaCodec.BufferInfo bi) {
            long delta = bi.presentationTimeUs - lastPresentationTimeUs;
            if (delta < 0) {
                return;
            }
            boolean isSyncFrame = !isAudio && (bi.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
            samples.add(new Sample(offset, bi.size));
            if (syncSamples != null && isSyncFrame) {
                syncSamples.add(samples.size());
            }

            delta = (delta * timeScale + 500000L) / 1000000L;
            lastPresentationTimeUs = bi.presentationTimeUs;
            if (!first) {
                sampleDurations.add(sampleDurations.size() - 1, delta);
                duration += delta;
            }
            first = false;
        }

        public void clearSample() {
            first = true;
            samples.clear();
            syncSamples.clear();
            sampleDurations.clear();
        }

        public ArrayList<Sample> getSamples() {
            return samples;
        }

        public long getDuration() {
            return duration;
        }

        public String getHandler() {
            return handler;
        }

        public AbstractMediaHeaderBox getMediaHeaderBox() {
            return headerBox;
        }

        public SampleDescriptionBox getSampleDescriptionBox() {
            return sampleDescriptionBox;
        }

        public long[] getSyncSamples() {
            if (syncSamples == null || syncSamples.isEmpty()) {
                return null;
            }
            long[] returns = new long[syncSamples.size()];
            for (int i = 0; i < syncSamples.size(); i++) {
                returns[i] = syncSamples.get(i);
            }
            return returns;
        }

        public int getTimeScale() {
            return timeScale;
        }

        public Date getCreationTime() {
            return creationTime;
        }

        public int getWidth() {
            return width;
        }

        public int getHeight() {
            return height;
        }

        public float getVolume() {
            return volume;
        }

        public ArrayList<Long> getSampleDurations() {
            return sampleDurations;
        }

        public boolean isAudio() {
            return isAudio;
        }

        public int getTrackId() {
            return trackId;
        }
    }

    private class Mp4Movie {
        private Matrix matrix = Matrix.ROTATE_0;
        private HashMap<Integer, Track> tracks = new HashMap<>();

        public Matrix getMatrix() {
            return matrix;
        }

        public HashMap<Integer, Track> getTracks() {
            return tracks;
        }

        public void addSample(int trackIndex, long offset, MediaCodec.BufferInfo bi) {
            Track track = tracks.get(trackIndex);
            track.addSample(offset, bi);
        }

        public void addTrack(MediaFormat format, boolean isAudio) {
            if (format != null) {
                if (isAudio) {
                    tracks.put(AUDIO_TRACK, new Track(tracks.size(), format, true));
                } else {
                    tracks.put(VIDEO_TRACK, new Track(tracks.size(), format, false));
                }
            }
        }

        public void removeTrack(int trackIndex) {
            tracks.remove(trackIndex);
        }
    }

    private class InterleaveChunkMdat implements Box {
        private boolean first = true;
        private ContainerBox parent;
        private ByteBuffer header = ByteBuffer.allocateDirect(16);
        private long contentSize = 1024 * 1024 * 1024;

        public ContainerBox getParent() {
            return parent;
        }

        public void setParent(ContainerBox parent) {
            this.parent = parent;
        }

        public void setContentSize(long contentSize) {
            this.contentSize = contentSize;
        }

        public long getContentSize() {
            return contentSize;
        }

        public String getType() {
            return "mdat";
        }

        public long getSize() {
            return header.limit() + contentSize;
        }

        public int getHeaderSize() {
            return header.limit();
        }

        private boolean isSmallBox(long contentSize) {
            return (contentSize + header.limit()) < 4294967296L;
        }

        public void getBox(WritableByteChannel writableByteChannel) {
            header.rewind();
            long size = getSize();
            if (isSmallBox(size)) {
                IsoTypeWriter.writeUInt32(header, size);
            } else {
                IsoTypeWriter.writeUInt32(header, 1);
            }
            header.put(IsoFile.fourCCtoBytes("mdat"));
            if (isSmallBox(size)) {
                header.put(new byte[8]);
            } else {
                IsoTypeWriter.writeUInt64(header, size);
            }
            header.rewind();

            try {
                writableByteChannel.write(header);
            } catch (IOException e) {
                mHandler.notifyRecordIOException(e);
            }
        }

        @Override
        public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
        }
    }

    private InterleaveChunkMdat mdat = null;
    private FileOutputStream fos = null;
    private FileChannel fc = null;
    private volatile long recFileSize = 0;
    private volatile long mdatOffset = 0;
    private volatile long flushBytes = 0;
    private HashMap<Track, long[]> track2SampleSizes = new HashMap<>();

    private void createMovie(File outputFile) {
        try {
            fos = new FileOutputStream(outputFile);
            fc = fos.getChannel();
            mdat = new InterleaveChunkMdat();
            mdatOffset = 0;

            FileTypeBox fileTypeBox = createFileTypeBox();
            fileTypeBox.getBox(fc);
            recFileSize += fileTypeBox.getSize();
        } catch (IOException e) {
            e.printStackTrace();
            mHandler.notifyRecordIOException(e);
        }
    }

    private void writeSampleData(ByteBuffer byteBuf, MediaCodec.BufferInfo bi, boolean isAudio) {
        int trackIndex = isAudio ? AUDIO_TRACK : VIDEO_TRACK;
        if (!mp4Movie.getTracks().containsKey(trackIndex)) {
            return;
        }

        try {
            if (mdat.first) {
                mdat.setContentSize(0);
                mdat.getBox(fc);
                mdatOffset = recFileSize;
                recFileSize += mdat.getHeaderSize();
                mdat.first = false;
            }

            mp4Movie.addSample(trackIndex, recFileSize, bi);
            byteBuf.position(bi.offset + (isAudio ? 0 : 4));
            byteBuf.limit(bi.offset + bi.size);
            if (!isAudio) {
                ByteBuffer size = ByteBuffer.allocateDirect(4);
                size.position(0);
                size.putInt(bi.size - 4);
                size.position(0);
                recFileSize += fc.write(size);
            }
            int writeBytes = fc.write(byteBuf);

            recFileSize += writeBytes;
            flushBytes += writeBytes;
            if (flushBytes > 64 * 1024) {
                fos.flush();
                flushBytes = 0;
            }
        } catch (IOException e) {
            e.printStackTrace();
            mHandler.notifyRecordIOException(e);
        }
    }

    private void finishMovie() {
        try {
            if (flushBytes > 0) {
                fos.flush();
                flushBytes = 0;
            }
            if (mdat.getSize() != 0) {
                // flush cached mdat box
                long oldPosition = fc.position();
                fc.position(mdatOffset);
                mdat.setContentSize(recFileSize - mdat.getHeaderSize() - mdatOffset);
                mdat.getBox(fc);
                fc.position(oldPosition);
                mdat.setContentSize(0);
                fos.flush();
            }

            for (Track track : mp4Movie.getTracks().values()) {
                List<Sample> samples = track.getSamples();
                long[] sizes = new long[samples.size()];
                for (int i = 0; i < sizes.length; i++) {
                    sizes[i] = samples.get(i).getSize();
                }
                track2SampleSizes.put(track, sizes);
            }

            Box moov = createMovieBox(mp4Movie);
            moov.getBox(fc);
            fos.flush();

            fc.close();
            fos.close();
            mp4Movie.getTracks().clear();
            track2SampleSizes.clear();
            recFileSize = 0;
            flushBytes = 0;
        } catch (IOException e) {
            mHandler.notifyRecordIOException(e);
        }
    }

    private FileTypeBox createFileTypeBox() {
        LinkedList<String> minorBrands = new LinkedList<>();
        minorBrands.add("isom");
        minorBrands.add("3gp4");
        return new FileTypeBox("isom", 0, minorBrands);
    }

    private long getTimescale(Mp4Movie mp4Movie) {
        long timescale = 0;
        if (!mp4Movie.getTracks().isEmpty()) {
            timescale = mp4Movie.getTracks().values().iterator().next().getTimeScale();
        }
        for (Track track : mp4Movie.getTracks().values()) {
            timescale = Math.gcd(track.getTimeScale(), timescale);
        }
        return timescale;
    }

    private MovieBox createMovieBox(Mp4Movie movie) {
        MovieBox movieBox = new MovieBox();
        MovieHeaderBox mvhd = new MovieHeaderBox();

        mvhd.setCreationTime(new Date());
        mvhd.setModificationTime(new Date());
        mvhd.setMatrix(Matrix.ROTATE_0);
        long movieTimeScale = getTimescale(movie);
        long duration = 0;

        for (Track track : movie.getTracks().values()) {
            long tracksDuration = track.getDuration() * movieTimeScale / track.getTimeScale();
            if (tracksDuration > duration) {
                duration = tracksDuration;
            }
        }

        mvhd.setDuration(duration);
        mvhd.setTimescale(movieTimeScale);
        mvhd.setNextTrackId(movie.getTracks().size() + 1);

        movieBox.addBox(mvhd);
        for (Track track : movie.getTracks().values()) {
            movieBox.addBox(createTrackBox(track, movie));
        }
        return movieBox;
    }

    private TrackBox createTrackBox(Track track, Mp4Movie movie) {
        TrackBox trackBox = new TrackBox();
        TrackHeaderBox tkhd = new TrackHeaderBox();

        tkhd.setEnabled(true);
        tkhd.setInMovie(true);
        tkhd.setInPreview(true);
        if (track.isAudio()) {
            tkhd.setMatrix(Matrix.ROTATE_0);
        } else {
            tkhd.setMatrix(movie.getMatrix());
        }
        tkhd.setAlternateGroup(0);
        tkhd.setCreationTime(track.getCreationTime());
        tkhd.setModificationTime(track.getCreationTime());
        tkhd.setDuration(track.getDuration() * getTimescale(movie) / track.getTimeScale());
        tkhd.setHeight(track.getHeight());
        tkhd.setWidth(track.getWidth());
        tkhd.setLayer(0);
        tkhd.setModificationTime(new Date());
        tkhd.setTrackId(track.getTrackId() + 1);
        tkhd.setVolume(track.getVolume());

        trackBox.addBox(tkhd);

        MediaBox mdia = new MediaBox();
        trackBox.addBox(mdia);
        MediaHeaderBox mdhd = new MediaHeaderBox();
        mdhd.setCreationTime(track.getCreationTime());
        mdhd.setModificationTime(track.getCreationTime());
        mdhd.setDuration(track.getDuration());
        mdhd.setTimescale(track.getTimeScale());
        mdhd.setLanguage("eng");
        mdia.addBox(mdhd);
        HandlerBox hdlr = new HandlerBox();
        hdlr.setName(track.isAudio() ? "SoundHandle" : "VideoHandle");
        hdlr.setHandlerType(track.getHandler());

        mdia.addBox(hdlr);

        MediaInformationBox minf = new MediaInformationBox();
        minf.addBox(track.getMediaHeaderBox());

        DataInformationBox dinf = new DataInformationBox();
        DataReferenceBox dref = new DataReferenceBox();
        dinf.addBox(dref);
        DataEntryUrlBox url = new DataEntryUrlBox();
        url.setFlags(1);
        dref.addBox(url);
        minf.addBox(dinf);

        Box stbl = createStbl(track);
        minf.addBox(stbl);
        mdia.addBox(minf);

        return trackBox;
    }

    private Box createStbl(Track track) {
        SampleTableBox stbl = new SampleTableBox();
        createStsd(track, stbl);
        createStts(track, stbl);
        createStss(track, stbl);
        createStsc(track, stbl);
        createStsz(track, stbl);
        createStco(track, stbl);
        return stbl;
    }

    private void createStsd(Track track, SampleTableBox stbl) {
        stbl.addBox(track.getSampleDescriptionBox());
    }

    private void createStts(Track track, SampleTableBox stbl) {
        TimeToSampleBox.Entry lastEntry = null;
        List<TimeToSampleBox.Entry> entries = new ArrayList<>();

        for (long delta : track.getSampleDurations()) {
            if (lastEntry != null && lastEntry.getDelta() == delta) {
                lastEntry.setCount(lastEntry.getCount() + 1);
            } else {
                lastEntry = new TimeToSampleBox.Entry(1, delta);
                entries.add(lastEntry);
            }
        }
        TimeToSampleBox stts = new TimeToSampleBox();
        stts.setEntries(entries);
        stbl.addBox(stts);
    }

    private void createStss(Track track, SampleTableBox stbl) {
        long[] syncSamples = track.getSyncSamples();
        if (syncSamples != null && syncSamples.length > 0) {
            SyncSampleBox stss = new SyncSampleBox();
            stss.setSampleNumber(syncSamples);
            stbl.addBox(stss);
        }
    }

    private void createStsc(Track track, SampleTableBox stbl) {
        SampleToChunkBox stsc = new SampleToChunkBox();
        stsc.setEntries(new LinkedList<SampleToChunkBox.Entry>());

        long lastOffset;
        int lastChunkNumber = 1;
        int lastSampleCount = 0;

        int previousWritedChunkCount = -1;

        int samplesCount = track.getSamples().size();
        for (int a = 0; a < samplesCount; a++) {
            Sample sample = track.getSamples().get(a);
            long offset = sample.getOffset();
            long size = sample.getSize();

            lastOffset = offset + size;
            lastSampleCount++;

            boolean write = false;
            if (a != samplesCount - 1) {
                Sample nextSample = track.getSamples().get(a + 1);
                if (lastOffset != nextSample.getOffset()) {
                    write = true;
                }
            } else {
                write = true;
            }
            if (write) {
                if (previousWritedChunkCount != lastSampleCount) {
                    stsc.getEntries().add(new SampleToChunkBox.Entry(lastChunkNumber, lastSampleCount, 1));
                    previousWritedChunkCount = lastSampleCount;
                }
                lastSampleCount = 0;
                lastChunkNumber++;
            }
        }
        stbl.addBox(stsc);
    }

    private void createStsz(Track track, SampleTableBox stbl) {
        SampleSizeBox stsz = new SampleSizeBox();
        stsz.setSampleSizes(track2SampleSizes.get(track));
        stbl.addBox(stsz);
    }

    private void createStco(Track track, SampleTableBox stbl) {
        ArrayList<Long> chunksOffsets = new ArrayList<>();
        long lastOffset = -1;
        for (Sample sample : track.getSamples()) {
            long offset = sample.getOffset();
            if (lastOffset != -1 && lastOffset != offset) {
                lastOffset = -1;
            }
            if (lastOffset == -1) {
                chunksOffsets.add(offset);
            }
            lastOffset = offset + sample.getSize();
        }
        long[] chunkOffsetsLong = new long[chunksOffsets.size()];
        for (int a = 0; a < chunksOffsets.size(); a++) {
            chunkOffsetsLong[a] = chunksOffsets.get(a);
        }

        StaticChunkOffsetBox stco = new StaticChunkOffsetBox();
        stco.setChunkOffsets(chunkOffsetsLong);
        stbl.addBox(stco);
    }
}