...
 
Commits (12)
......@@ -4,19 +4,19 @@ TEST_PROG = camogm_test
PHPSCRIPTS = camogmstate.php $(GUIDIR)/camogmgui.php $(GUIDIR)/camogmgui.css $(GUIDIR)/camogmgui.js $(GUIDIR)/camogm_interface.php \
$(GUIDIR)/SpryTabbedPanels.css $(GUIDIR)/SpryTabbedPanels.js $(GUIDIR)/xml_simple.php $(GUIDIR)/SpryCollapsiblePanel.css \
$(GUIDIR)/SpryCollapsiblePanel.js
CONFIGS = qt_source
CONFIGS = qt_source qt_audio
IMAGES = $(GUIDIR)/images/filebrowser-01.gif $(GUIDIR)/images/filebrowser-bottom-01.gif $(GUIDIR)/images/png_white_30.png \
$(GUIDIR)/images/record.gif $(GUIDIR)/images/reload.png $(GUIDIR)/images/stop.gif $(GUIDIR)/images/create_folder.png \
$(GUIDIR)/images/divider.png $(GUIDIR)/images/folder.gif $(GUIDIR)/images/help.png $(GUIDIR)/images/quicktime.png \
$(GUIDIR)/images/rec_folder.png $(GUIDIR)/images/up_folder.gif $(GUIDIR)/images/play_audio.png $(GUIDIR)/images/hdd.png
SRCS = camogm.c camogm_ogm.c camogm_jpeg.c camogm_mov.c camogm_kml.c camogm_read.c index_list.c camogm_align.c
SRCS = camogm.c camogm_ogm.c camogm_jpeg.c camogm_mov.c camogm_kml.c camogm_read.c index_list.c camogm_align.c camogm_audio.c thelper.c
TEST_SRC = camogm_test.c
OBJS = $(SRCS:.c=.o)
CFLAGS += -Wall -I$(STAGING_DIR_HOST)/usr/include-uapi
LDLIBS += -logg -pthread -lm
LDLIBS += -logg -pthread -lm -lasound
INSTALL = install
INSTMODE = 0755
......
This diff is collapsed.
......@@ -22,10 +22,13 @@
#include <pthread.h>
#include <stdbool.h>
#include <ogg/ogg.h>
#include "ogmstreams.h"
#include <elphel/exifa.h>
#include <elphel/c313a.h>
#include <elphel/x393_devices.h>
#include <sys/uio.h>
#include "ogmstreams.h"
#include "camogm_audio.h"
#define CAMOGM_FRAME_NOT_READY 1 ///< frame pointer valid, but not yet acquired
#define CAMOGM_FRAME_INVALID 2 ///< invalid frame pointer
......@@ -67,6 +70,10 @@
#define COMMAND_LOOP_DELAY 500000
/** @brief File can be split up to this number of chunks */
#define FILE_CHUNKS_NUM 8
/** @brief container_of macro as in the kernel */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**
* @enum state_flags
......@@ -215,10 +222,18 @@ typedef struct {
double start_after_timestamp; ///< delay recording start to after frame timestamp
int max_frames;
int set_max_frames;
int frames_per_chunk;
int set_frames_per_chunk; ///< quicktime - index for fast forward?
int frameno;
int *frame_lengths;
int frames_per_chunk; ///< QuickTime, the number of samples (images or audio samples) in a chunk
unsigned int chunk_frame_cntr; ///< QuickTime, number of video frames recorded in current chunk
int set_frames_per_chunk; ///< QuickTime, index for fast forward (sample-to-chunk atom)
int frameno; ///< total image frame counter, does not include audio samples
unsigned long *frame_lengths; ///< QuickTime; pointer to an array of frame lengths which includes both image frames
///< and audio samples if audio recording is enabled. MSB of each entry indicates
///< the type of frame this length relates to: if MSB = 0 then this is images frame and
///< if MSB = 1 then this is audio sample. Stealing one bit from the length field
///< effectively limits the size of one frame to 2 GiB which should be more than enough
///< for all purposes.
long frame_index; ///< QuickTime; total frame counter including audio samples (if enabled), this is used
///< as an index for frame_lengths array
off_t frame_data_start; ///< Quicktime (and else?) - frame data start (0xff 0xd8...)
ogg_int64_t time_unit;
int formats; ///< bitmask of used (initialized) formats
......@@ -253,9 +268,13 @@ typedef struct {
int rawdev_op; ///< flag indicating writing to raw device
rawdev_buffer rawdev; ///< contains pointers to raw device buffer
unsigned int active_chn; ///< bitmask of active sensor ports
unsigned int active_chn_mask; ///< bitmask of enabled sensor ports; used to save enabled/disabled ports between
///< consecutive starts/stops as active ports list is updated on each start
uint16_t sock_port; ///< command socket port number
struct writer_params writer_params; ///< contains control parameters for writing thread
unsigned int error_stat[SENSOR_PORTS][CAMOGM_ERRNUM]; ///< collect statistics about errors
struct audio audio; ///< various parameters related to audio
} camogm_state;
extern int debug_level;
......
This diff is collapsed.
/**
* @file camogm_audio.h
* @brief Provides audio interface for camogm
* @copyright Copyright (C) 2017 Elphel Inc.
* @author AUTHOR <EMAIL>
*
* @par License:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CAMOGM_AUDIO_H
#define _CAMOGM_AUDIO_H
#include <sys/time.h>
#include <alsa/asoundlib.h>
#define SAMPLE_RATE 44100 ///< default sampling rate
#define SAMPLE_CHANNELS 2 ///< default number of audio channels
#define SAMPLE_TIME 200 ///< restrict ALSA to have this period, in milliseconds
#define BUFFER_TIME 1000 ///< approximate ALSA buffer duration, in milliseconds
#define DEFAULT_SND_DEVICE "plughw:0,0"
#define AUDIO_CHANNELS_MIN 1
#define AUDIO_CHANNELS_MAX 2
#define AUDIO_RATE_MIN 11025
#define AUDIO_RATE_MAX 44100
#define DEFAULT_AUDIO_VOLUME 0xffff ///< absolute maximum audio volume
/**
* @brief Audio recording context related to stream management.
* Members of this structure should not be used outside audio module.
*/
struct context_audio {
char *sbuffer; ///< buffer for audio samples
long sbuffer_len; ///< total length of audio buffer, in audio frames
long sbuffer_pos; ///< pointer to current write position in audio buffer, in frames
long read_frames; ///< read granularity, in frames
long sample_time; ///< duration of one chunk of audio data, in ms
struct timeval time_start; ///< start time, set only when stream starts and updated with each new file
struct timeval time_last; ///< calculated time of last audio sample (this value is not taken from ALSA)
long rem_samples; ///< remaining samples
long lost_frames; ///< the number of frames lost after buffer overrun
char *xrun_buffer; ///< temporary storage for the data saved in buffer befor xrun
long xrun_pos; ///< number of samples in xrun buffer
long xrun_append; ///< save in buffer this number of frames after xrun, all other frames in chunk
///< will be silence
snd_pcm_format_t audio_format; ///< format of audio samples as defined in 'enum snd_pcm_format_t'
snd_pcm_t *capture_hnd; ///< ALSA PCM handle
};
/**
* @brief Various parameters related to audio recording.
*/
struct audio {
int audio_enable; ///< flag indicating that audio is enabled
int audio_rate; ///< sample rate, in Hz
int audio_channels; ///< number of channels
int audio_volume; ///< volume set in range [0..0xFFFF]
int sync_port; ///< synch audio stream to this sensor port
int set_audio_enable; ///< temporary storage for new value
int set_audio_rate; ///< temporary storage for new value
int set_audio_channels; ///< temporary storage for new value
int set_audio_volume; ///< temporary storage for new value
long audio_frameno; ///< frame number counter, used as index in frames_len array
long audio_samples; ///< samples counter
unsigned long *frames_len; ///< indexes of audio frames
int *audio_samples_to_chunk; ///< an array of chunks, contains sample count in each chunk
char *dev_name; ///< the name of audio device to use
struct context_audio ctx_a; ///< current audio context
struct timeval ts_audio; ///< time stamp when audio stream started
struct timeval ts_video; ///< time stamp of each new frame
struct timeval ts_video_start; ///< time stamp of starting video frame
int frame_period_us; ///< video frame period measured for #sync_port, in microseconds
unsigned long audio_skip_samples; ///< skip this number audio frames to sync to video
int begin_of_stream_with_audio; ///< flag indicating that A/V sync is in progress
int audio_trigger; ///< indicates the beginning of audio recording to make some initial set ups
bool save_data; ///< flag indicating that audio data should be recorded, otherwise audio frames should be
///< stored in buffer for delayed recording
unsigned int sleep_period_us; ///< sleep period between frames while processing audio stream, in microseconds
void (*get_fpga_time)(const struct audio *audio, struct timeval *tv);//< callback function which can get FPGA time
int (*write_samples)(struct audio *audio, void *buff, long len, long slen); ///< callback function which actually write data to file, this must be set
};
void audio_init_hw(struct audio *audio, bool restart);
void audio_init_sw(struct audio *audio, bool restart, int frames);
void audio_process(struct audio *audio);
void audio_finish(struct audio *audio, bool reset);
void audio_set_volume(int nvolume);
unsigned long audio_get_hw_buffer_max(void);
#endif /* _CAMOGM_AUDIO_H */
......@@ -153,7 +153,7 @@ static int save_state_file(const camogm_state *state)
/**
* @brief Initialize synchronization resources for disk writing thread and then start this thread. This function
* is call each time JPEG format is set or changed, thus we need to check the state of writing thread before
* is called each time JPEG format is set or changed, thus we need to check the state of writing thread before
* initialization to prevent spawning multiple threads.
* @param[in] state a pointer to a structure containing current state
* @return 0 if initialization was successful and negative value otherwise
......@@ -202,19 +202,21 @@ int camogm_init_jpeg(camogm_state *state)
*/
void camogm_free_jpeg(camogm_state *state)
{
pthread_cond_destroy(&state->writer_params.main_cond);
pthread_cond_destroy(&state->writer_params.writer_cond);
pthread_mutex_destroy(&state->writer_params.writer_mutex);
// terminate writing thread
pthread_mutex_lock(&state->writer_params.writer_mutex);
state->writer_params.exit_thread = true;
pthread_cond_signal(&state->writer_params.writer_cond);
pthread_mutex_unlock(&state->writer_params.writer_mutex);
pthread_join(state->writer_params.writer_thread, NULL);
state->writer_params.exit_thread = false;
if (state->writer_params.state == STATE_RUNNING) {
pthread_cond_destroy(&state->writer_params.main_cond);
pthread_cond_destroy(&state->writer_params.writer_cond);
pthread_mutex_destroy(&state->writer_params.writer_mutex);
deinit_align_buffers(state);
// terminate writing thread
pthread_mutex_lock(&state->writer_params.writer_mutex);
state->writer_params.exit_thread = true;
pthread_cond_signal(&state->writer_params.writer_cond);
pthread_mutex_unlock(&state->writer_params.writer_mutex);
pthread_join(state->writer_params.writer_thread, NULL);
state->writer_params.exit_thread = false;
deinit_align_buffers(state);
}
}
/** Calculate the total length of current frame */
......
This diff is collapsed.
......@@ -20,7 +20,9 @@
#include "camogm.h"
int camogm_init_mov(void);
#define SAMPLE_DESCR_ID_AUDIO 1 //< audio sample description ID, used in sample-to-chunk atom
int camogm_init_mov(camogm_state *state);
int camogm_start_mov(camogm_state *state);
int camogm_frame_mov(camogm_state *state);
int camogm_end_mov(camogm_state *state);
......
......@@ -58,6 +58,24 @@
$mode = 0777;
$sensor_ports = elphel_num_sensors();
$default_imgsrv_port = 2323;
// audio file for test playback; the directory with several test files should be installed with ALSA
$test_audio_file = "/usr/share/sounds/alsa/Front_Center.wav";
// the list of audio volume values
$audio_vol_list = array(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100);
/** Find closest value from the list of audio volumes */
function get_closest_volume($vol)
{
global $audio_vol_list;
$closest = null;
foreach ($audio_vol_list as $elem) {
if ($closest === null || abs($vol - $closest) > abs($elem - $vol))
$closest = $elem;
}
return $closest;
}
/** Draw single buffer usage bar */
function draw_buffer_bar($port, $ports_num)
......@@ -220,9 +238,8 @@
fprintf($fcmd, "audio=off;\n");
fprintf($fcmd, "audio_format=%s/%s;\n", $_POST['audio_sample_rate'], $_POST['audio_channels']);
fprintf($fcmd, "audio_volume=%s;\n", $_POST['audio_volume']);
// TODO: set volume here!
if ($_POST['audio_syncmode'] == "normal")
fprintf($fcmd, "allow_sync=disable;\n");
else
......@@ -322,10 +339,10 @@
$xml_geotagging_period = substr($logdata[0]['kml_period'], 0, strlen($logdata[0]['kml_period']));
// Audio Recording
$xml_audiorecording_enabled = substr($logdata[0]['audio_enable'], 1, strlen($logdata[0]['audio_enable'])-2);
$xml_audio_channels = substr($logdata[0]['audio_channels'], 1, strlen($logdata[0]['audio_channels'])-2);
$xml_audio_rate = substr($logdata[0]['audio_rate'], 1, strlen($logdata[0]['audio_rate'])-2);
$xml_audio_volume = substr($logdata[0]['audio_volume'], 1, strlen($logdata[0]['audio_volume'])-2);
$xml_audiorecording_enabled = substr($logdata[0]['audio_enable'], 0, strlen($logdata[0]['audio_enable']));
$xml_audio_channels = substr($logdata[0]['audio_channels'], 0, strlen($logdata[0]['audio_channels']));
$xml_audio_rate = substr($logdata[0]['audio_rate'], 0, strlen($logdata[0]['audio_rate']));
$xml_audio_volume = substr($logdata[0]['audio_volume'], 0, strlen($logdata[0]['audio_volume']));
$xml_audio_syncmode = substr($logdata[0]['allow_sync'], 1, strlen($logdata[0]['allow_sync'])-2);
// Get per sensor port parameters
......@@ -652,11 +669,10 @@
<div class="TabbedPanelsContent">
<!-- Sound -->
<b>Audio Recording:</b><br />
<div class="small">requires external USB soundcard</div>
<p style="color:red;">not operational yet!</p>
<div class="small">requires external USB soundcard or headset</div>
Detected Audio Hardware: <span id="ajax_detected_audio_hardware">loading...</span> <a href="#" onClick="check_audio_hardware();"><img src="images/reload.png" style="bottom:-2px; position:relative;"></a><br />
<br />
Test Audio Playback: <a href="#" onClick="test_audio_playback('/var/hdd/Congas.wav');"><img src="images/play_audio.png" style="position:relative; top:3px;"></a><br />
Test Audio Playback: <a href="#" onClick="test_audio_playback(' <?php echo $test_audio_file; ?> ');"><img src="images/play_audio.png" style="position:relative; top:3px;"></a><br />
<br />
<form method="POST" id="audioform">
<table cellspacing="5px">
......@@ -670,24 +686,23 @@
</td></tr>
<tr><td>Channels:</td><td>
<select name="audio_channels" id="audio_channels" disabled>
<option <? if ($xml_audio_channels == 2) echo "selected"; ?>>stereo</option>
<option <? if ($xml_audio_channels == 1) echo "selected"; ?>>mono</option>
<option <? if ($xml_audio_channels == 2) echo "selected"; ?> value="2">stereo</option>
<option <? if ($xml_audio_channels == 1) echo "selected"; ?> value="1">mono</option>
</select>
</td></tr>
<tr><td>Volume:</td><td>
<select name="audio_volume" id="audio_volume" disabled>
<option value="100" <? if ($xml_audio_volume == 100) echo "selected"; ?>>100%</option>
<option value="90" <? if ($xml_audio_volume == 90) echo "selected"; ?>>90%</option>
<option value="80" <? if ($xml_audio_volume == 80) echo "selected"; ?>>80%</option>
<option value="70" <? if ($xml_audio_volume == 70) echo "selected"; ?>>70%</option>
<option value="60" <? if ($xml_audio_volume == 60) echo "selected"; ?>>60%</option>
<option value="50" <? if ($xml_audio_volume == 50) echo "selected"; ?>>50%</option>
<option value="40" <? if ($xml_audio_volume == 40) echo "selected"; ?>>40%</option>
<option value="30" <? if ($xml_audio_volume == 30) echo "selected"; ?>>30%</option>
<option value="20" <? if ($xml_audio_volume == 20) echo "selected"; ?>>20%</option>
<option value="10" <? if ($xml_audio_volume == 10) echo "selected"; ?>>10%</option>
<option value="0" <? if ($xml_audio_volume == 0) echo "selected"; ?>>0%</option>
</select> volume not working, please ignore
<?php
$closest = get_closest_volume($xml_audio_volume);
foreach (array_reverse($audio_vol_list) as $vol) {
if ($closest == $vol)
$sel = "selected";
else
$sel = "";
echo "<option value=\"" . $vol . "\" " . $sel . ">" . $vol . "%</option>\n";
}
?>
</select>
</td></tr>
<tr><td>Sync Mode:</td><td>
<select name="audio_syncmode" id="audio_syncmode" disabled>
......
{'moov
{'mvhd
00 # version
000000 # Flags
!time # Creation time
!time # Modification time
!timescale # Time Scale - time unints per second
!duration # Duration - in time units
00010000 # Preferred rate
0100 # Preferred volume
00000000 00000000 0000 # (reserved)
00010000 00000000 00000000 # Matrix [a b u]
00000000 00010000 00000000 # Matrix [c d v]
00000000 00000000 40000000 # Matrix [x y w]
00000000 # Preview Time
00000000 # Preview Duration
00000000 # Poster Time
00000000 # Selection Time
00000000 # Selection Duration
00000000 # Current Time
00000003 # Next Track ID
} # 'mvhd
{'trak
{'tkhd
00 # version
00000f # Flags
!time # Creation time
!time # Modification time
00000001 # Track ID
00000000 # (reserved)
!duration # Duration
00000000 00000000 # (reserved)
0000 # Layer
0000 # Alternate group
0000 # Volume
0000 # (reserved)
00010000 00000000 00000000 # Matrix [a b u]
00000000 00010000 00000000 # Matrix [c d v]
00000000 00000000 40000000 # Matrix [x y w]
!width 0000 # Track width
!height 0000 # Track height
} # 'tkhd
{'mdia
{'mdhd
00 # version
000000 # Flags
!time # Creation time
!time # Modification time
!timescale # Time Scale
!duration # Duration
0000 # Language
0000 # Quality
} # 'mdhd
{'hdlr
00 # version
000000 # Flags
'mhlr # Component type
'vide # Component subtype
'appl # Component manufacturer
00000000 # Component Flags
0001001f # Component Flags Mask
19 'Apple Video Media Handler # Component Name
} # 'hdlr
{'minf
{'vmhd
00 # version
000001 # Flags
0040 # Graphics Mode
8000 8000 8000 # Opcolor
} # 'vmhd
{'hdlr
00 # version
000000 # Flags
'dhlr # Component type
'alis # Component subtype
'appl # Cmponent manufacturer
00000001 # Component Flags
00010026 # Component Flags Mask
18 'Apple Alias Data Handler # Component Name
} # 'hdlr
{'dinf
{'dref
00 # version
000000 # Flags (1 - the same file???)
00000001 # Number of entries
0000000c 616c6973 00000001 # Data references
}
} # 'dinf
{'stbl
{'stsd
00 # version
000000 # Flags
00000001 # Number of entries
00000056 # Sample description size
'jpeg # Data format
00000000 0000 # (reserved)
0001 # Data reference index
0000 # Version
0000 # Revision Level
'appl # Vendor
00000000 # Temporal quality
00000200 # Spatial quality
!width # Width
!height # Height
00480000 # Horizontal resolution
00480000 # Vertical resolution
00000000 # Data size
0001 # Frame count
0c 'Photo - JPEG # Compressor Name
00000000 00000000 00000000 00000000 000000 # padding
0018 # Color depth (24)
ffff # Color table ID
} #'stsd
{'stts
00 # version
000000 # Flags
00000001 # Number of entries
!nframes !frame_duration # TIME-TO-SAMPLE table (was 0x0a 0x28) - time units/sample
}
{'stsc
00 # version
000000 # Flags
00000001 # Number of entries
# 00000001 !nframes 00000001 # SAMPLE-TO-CHUNK table - all in one chunk
00000001 !samples_chunk 00000001 # SAMPLE-TO-CHUNK table - each sample it new chunk
}
{'stsz
00 # version
000000 # Flags
00000000 # Sample Size ** Did not work with non-zero!
!nframes # Number of entries
!sample_sizes # nframes of 4-byte words
}
{'stco
00 # version
000000 # Flags
# 00000001 # Number of entries
# !mdata #
# !nframes # Number of entries
!chunk_offsets #
}
} #'stbl
} #'minf
} # 'mdia
# {'udta
# 00000000
# }
} #'trak
# sound track
{'trak
{'tkhd
00 # Version
00000f # Flags
!time # Creation time
!time # Modification time
00000002 # Track ID
00000000 # (reserved)
!duration # Duration (correct)
00000000 # (reserved)
00000000 # (reserved)
0000 # Layer
0000 # Alternate group
0100 # Volume
0000 # (reserved)
00010000 00000000 00000000 # Matrix [a b u]
00000000 00010000 00000000 # Matrix [c d v]
00000000 00000000 40000000 # Matrix [x y w]
00000000 # Track width
00000000 # Track height
} #'tkhd
{'mdia
{'mdhd # - required
00 # version
000000 # Flags
!time # Creation time
!time # Modification time
!audio_timescale # Time Scale - 44100 // ???
!audio_duration # Duration in units of the time scale
0000 # Language
0000 # Quality
} # 'mdhd
{'hdlr # - not required
00 # version
000000 # Flags
'mhlr # Component type
'soun # Component subtype
00000000 # Component manufacturer
00000000 # Component Flags
00000000 # Component Flags Mask
00 # Component name
# 'appl # Component manufacturer
# 00000001 # Component Flags
# 00010026 # Component Flags Mask
# 18 'Apple Sound Data Handler # Component Name
} # 'hdlr
{'minf
{'smhd
00 # version
000000 # Flags
0000 # Balance
0000 # Reserved
} # 'smhd
{'hdlr
00 # version
000000 # Flags
'dhlr # Component type
'alis # Component subtype
00000000 # Component manufacturer
# 'niko # Component manufacturer
00000000 # Component Flags
00000000 # Component Flags Mask
00 #
# 00000001 # Component Flags
# 00010026 # Component Flags Mask
#18 'Apple Alias Data Handler # Component Name
} # 'hdlr
{'dinf
{'dref
00 # version
000000 # Flags (1 - the same file???)
00000001 # Number of entries
{'alis
00000001
}
}
} # 'dinf
{'stbl # Sample Table atom
{'stsd # Sample Description atom
00 # Version
000000 # Flags
00000001 # Number of entries
{'sowt # 16 bit LE, Data format
00000000 0000 # (reserved)
0001 # Data reference index
0001 # Version
0000 # Revision level
00000000 # Vendor
!audio_channels # 2b, Number of channels
0010 # Sample size, == 0x10 == 16 bit
0000 # Compression ID
0000 # Packet size
!audio_rate # 4b, Sample rate - unsigned 16.16 FP
# new fields...
00000001 # samples per packets - always 1
00000002 # number of bytes in a sample for a single channel
!audio_bytes_per_frame # bytes per frame - 2 * channels
00000002 # bytes per sample - 2
# 00000000 # Terminator
# 00000000
}
} # 'stsd
{'stts # Time-to-sample atom - from the "Some Useful Examples: Creating Audio Tracks at 44.1 kHz"
00 # version
000000 # Flags
# !audio_stts #
00000001 # Number of entries
!audio_samples # Samples count
00000001 # Sample duration
}
{'stsc # Sample-to-chunk atom - OK
00 # version
000000 # Flags
!audio_stsc #
# 00000000 # Number of entries
#[ 00000000 # the first chunk number using this table entry (it starts from 1 in video)
#... 00000000 # the number of samples per chunk
#] 00000000 # sample description ID; defines the entry in stsd table which describes this chunk (it starts from 1 in video)
}
{'stsz # Sample size atom
00 # version
000000 # Flags
!audio_stsz # Sample Size - 2 for Mono, 4 for Stereo
# 00000000 # Number of entries; this table should be empty if all the samples are the same size
}
{'stco # Chunk offset atom
00 # version
000000 # Flags
!audio_stco #
}
} # 'stbl
} # 'minf
} #'mdia
{'tref # Track reference atom, identifies this track as related to video track
{'sync
00000001 # ID of the related video track
} # 'sync
} # 'tref
} #'trak
# {'udta
# {'WLOC
# 0017
# 0020
# }
# 00000000 # optional terminating 32-bit value, see description of User Data Atom
# }
}
!data_size
'mdat
......@@ -151,7 +151,7 @@
0017
0020
}
00000000 # ???
00000000 # optional terminating 32-bit value, see description of User Data Atom
}
}
!data_size
......
/**
* @file thelper.c
* @brief Various helper functions to work with timeval structures
* @copyright Copyright (C) 2017 Elphel Inc.
* @author AUTHOR <EMAIL>
*
* @par License:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "thelper.h"
/**
* Make sure that time represented by timeval structure has correct format, i.e.
* the value of microseconds does not exceed 1000000.
* @param tv time values to normalize
* @return None
*/
void time_normalize(struct timeval *tv)
{
tv->tv_sec += tv->tv_usec / 1000000;
tv->tv_usec = tv->tv_usec % 1000000;
}
/**
* Compare two times represented by timeval structure
* @param t1 first time value
* @param t2 second time value
* @return 1 if t1 > t2, 0 in case values are equal and -1 if t1 < t2
*/
int time_comp(struct timeval *t1, struct timeval *t2)
{
if (t1->tv_sec > t2->tv_sec)
return 1;
if (t1->tv_sec == t2->tv_sec) {
if (t1->tv_usec > t2->tv_usec)
return 1;
if (t1->tv_usec == t2->tv_usec)
return 0;
}
return -1;
}
/**
* @file thelper.h
* @brief Various helper functions to work with timeval structures
* @copyright Copyright (C) 2017 Elphel Inc.
* @author AUTHOR <EMAIL>
*
* @par License:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _THELPER_H
#define _THELPER_H
#include <sys/time.h>
void time_normalize(struct timeval *tv);
int time_comp(struct timeval *t1, struct timeval *t2);
/**
* Convert time represented by timeval structure to time in microseconds
* @param tv time value to convert
* @return time in microseconds
*/
inline unsigned long long time_to_us(const struct timeval *tv)
{
unsigned long long t;
t = tv->tv_sec * 1000000;
t += tv->tv_usec;
return t;
}
/**
* Convert time in microseconds to time represented by timeval structure
* @param us time in microseconds
* @return time in timeval structure
*/
inline struct timeval us_to_time(unsigned long us)
{
struct timeval tv;
tv.tv_sec = us / 1000000;
tv.tv_usec = us % 1000000;
return tv;
}
#endif /* _THELPER_H */