hawkeye camera support(not tested)
This commit is contained in:
498
framework/Inspectron.HawkEye/RTSP/Server/CJOCh264encoder.cs
Normal file
498
framework/Inspectron.HawkEye/RTSP/Server/CJOCh264encoder.cs
Normal file
@@ -0,0 +1,498 @@
|
||||
/* * CJOCh264encoder.cpp
|
||||
*
|
||||
* Created on: Aug 17, 2014
|
||||
* Author: Jordi Cenzano (www.jordicenzano.name)
|
||||
*/
|
||||
|
||||
/*
|
||||
* CJOCh264encoder.h
|
||||
*
|
||||
* Created on: Aug 17, 2014
|
||||
* Author: Jordi Cenzano (www.jordicenzano.name)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//C++ TO C# CONVERTER NOTE: The following #define macro was replaced in-line:
|
||||
//ORIGINAL LINE: #define BUFFER_SIZE_BYTES (24/8)
|
||||
|
||||
//! h264 encoder class
|
||||
/*!
|
||||
It is used to create the h264 compliant stream
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Inspectron.HawkEye.RTSP.Server
|
||||
{
|
||||
public class CJOCh264encoder : CJOCh264bitstream
|
||||
{
|
||||
|
||||
/**
|
||||
* Allowed sample formats
|
||||
*/
|
||||
public enum enSampleFormat
|
||||
{
|
||||
SAMPLE_FORMAT_YUV420p //!< SAMPLE_FORMAT_YUV420p
|
||||
}
|
||||
|
||||
|
||||
public List<byte> m_pOutFile = null;
|
||||
public byte[] sps = null;
|
||||
public byte[] pps = null;
|
||||
public byte[] nal = null;
|
||||
|
||||
/*!Set the used Y macroblock size for I PCM in YUV420p */
|
||||
private const int MACROBLOCK_Y_WIDTH = 16;
|
||||
private const int MACROBLOCK_Y_HEIGHT = 16;
|
||||
|
||||
/*!Set time base in Hz */
|
||||
private const int TIME_SCALE_IN_HZ = 27000000;
|
||||
|
||||
/*!Pointer to pixels */
|
||||
private class YUV420p_frame_t
|
||||
{
|
||||
public byte[] pYCbCr;
|
||||
}
|
||||
|
||||
/*! Frame */
|
||||
private class frame_t
|
||||
{
|
||||
public enSampleFormat sampleformat; //!< Sample format
|
||||
public uint nYwidth; //!< Y (luminance) block width in pixels
|
||||
public uint nYheight; //!< Y (luminance) block height in pixels
|
||||
public uint nCwidth; //!< C (Crominance) block width in pixels
|
||||
public uint nCheight; //!< C (Crominance) block height in pixels
|
||||
|
||||
public uint nYmbwidth; //!< Y (luminance) macroblock width in pixels
|
||||
public uint nYmbheight; //!< Y (luminance) macroblock height in pixels
|
||||
public uint nCmbwidth; //!< Y (Crominance) macroblock width in pixels
|
||||
public uint nCmbheight; //!< Y (Crominance) macroblock height in pixels
|
||||
|
||||
public YUV420p_frame_t yuv420pframe = new YUV420p_frame_t(); //!< Pointer to current frame data
|
||||
public uint nyuv420pframesize; //!< Size in bytes of yuv420pframe
|
||||
}
|
||||
|
||||
/*! The frame var*/
|
||||
private frame_t m_frame = new frame_t();
|
||||
|
||||
/*! The frames per second var*/
|
||||
private uint m_nFps;
|
||||
|
||||
/*! Number of frames sent to the output */
|
||||
private uint m_lNumFramesAdded;
|
||||
|
||||
|
||||
|
||||
//! Frees the frame yuv420pframe allocated memory
|
||||
|
||||
//Free the allocated video frame mem
|
||||
private void free_video_src_frame()
|
||||
{
|
||||
if (m_frame.yuv420pframe.pYCbCr != null)
|
||||
{
|
||||
//C++ TO C# CONVERTER TODO TASK: The memory management function 'free' has no equivalent in C#:
|
||||
// free(m_frame.yuv420pframe.pYCbCr);
|
||||
}
|
||||
|
||||
//C++ TO C# CONVERTER TODO TASK: The memory management function 'memset' has no equivalent in C#:
|
||||
// memset(m_frame, 0, sizeof(frame_t));
|
||||
}
|
||||
|
||||
//! Allocs the frame yuv420pframe memory according to the frame properties
|
||||
|
||||
//Alloc mem to store a video frame
|
||||
private void alloc_video_src_frame()
|
||||
{
|
||||
if (m_frame.yuv420pframe.pYCbCr != null)
|
||||
{
|
||||
throw new System.Exception("Error: null values in frame");
|
||||
}
|
||||
|
||||
uint nYsize = m_frame.nYwidth * m_frame.nYheight;
|
||||
uint nCsize = m_frame.nCwidth * m_frame.nCheight;
|
||||
m_frame.nyuv420pframesize = nYsize + nCsize + nCsize;
|
||||
|
||||
m_frame.yuv420pframe.pYCbCr = new byte[m_frame.nyuv420pframesize];
|
||||
|
||||
if (m_frame.yuv420pframe.pYCbCr == null)
|
||||
{
|
||||
throw new System.Exception("Error: memory alloc");
|
||||
}
|
||||
}
|
||||
|
||||
//! Creates SPS NAL and add it to the output
|
||||
/*!
|
||||
\param nImW Frame width in pixels
|
||||
\param nImH Frame height in pixels
|
||||
\param nMbW macroblock width in pixels
|
||||
\param nMbH macroblock height in pixels
|
||||
\param nFps frames x second (tipical values are: 25, 30, 50, etc)
|
||||
\param nSARw Indicates the horizontal size of the sample aspect ratio (tipical values are:1, 4, 16, etc)
|
||||
\param nSARh Indicates the vertical size of the sample aspect ratio (tipical values are:1, 3, 9, etc)
|
||||
*/
|
||||
|
||||
//Creates and saves the NAL SPS (including VUI) (one per file)
|
||||
private void create_sps(uint nImW, uint nImH, uint nMbW, uint nMbH, uint nFps, uint nSARw, uint nSARh)
|
||||
{
|
||||
add4bytesnoemulationprevention(0x000001); // NAL header
|
||||
addbits(0x0, 1); // forbidden_bit
|
||||
addbits(0x3, 2); // nal_ref_idc
|
||||
addbits(0x7, 5); // nal_unit_type : 7 ( SPS )
|
||||
addbits(0x42, 8); // profile_idc = baseline ( 0x42 )
|
||||
addbits(0x0, 1); // constraint_set0_flag
|
||||
addbits(0x0, 1); // constraint_set1_flag
|
||||
addbits(0x0, 1); // constraint_set2_flag
|
||||
addbits(0x0, 1); // constraint_set3_flag
|
||||
addbits(0x0, 1); // constraint_set4_flag
|
||||
addbits(0x0, 1); // constraint_set5_flag
|
||||
addbits(0x0, 2); // reserved_zero_2bits /* equal to 0 */
|
||||
addbits(0x0a, 8); // level_idc: 3.1 (0x0a)
|
||||
addexpgolombunsigned(0); // seq_parameter_set_id
|
||||
addexpgolombunsigned(0); // log2_max_frame_num_minus4
|
||||
addexpgolombunsigned(0); // pic_order_cnt_type
|
||||
addexpgolombunsigned(0); // log2_max_pic_order_cnt_lsb_minus4
|
||||
addexpgolombunsigned(0); // max_num_refs_frames
|
||||
addbits(0x0, 1); // gaps_in_frame_num_value_allowed_flag
|
||||
|
||||
uint nWinMbs = nImW / nMbW;
|
||||
addexpgolombunsigned(nWinMbs - 1); // pic_width_in_mbs_minus_1
|
||||
uint nHinMbs = nImH / nMbH;
|
||||
addexpgolombunsigned(nHinMbs - 1); // pic_height_in_map_units_minus_1
|
||||
|
||||
addbits(0x1, 1); // frame_mbs_only_flag
|
||||
addbits(0x0, 1); // direct_8x8_interfernce
|
||||
addbits(0x0, 1); // frame_cropping_flag
|
||||
// addbits(0x1, 1); // vui_parameter_present
|
||||
addbits(0x0, 1); // vui_parameter_present
|
||||
|
||||
//VUI parameters (AR, timming)
|
||||
// addbits(0x1, 1); //aspect_ratio_info_present_flag
|
||||
// addbits(0xFF, 8); //aspect_ratio_idc = Extended_SAR
|
||||
|
||||
//AR
|
||||
// addbits(nSARw, 16); //sar_width
|
||||
// addbits(nSARh, 16); //sar_height
|
||||
|
||||
// addbits(0x0, 1); //overscan_info_present_flag
|
||||
// addbits(0x0, 1); //video_signal_type_present_flag
|
||||
// addbits(0x0, 1); //chroma_loc_info_present_flag
|
||||
// addbits(0x1, 1); //timing_info_present_flag
|
||||
|
||||
// uint nnum_units_in_tick = TIME_SCALE_IN_HZ / (2 * nFps);
|
||||
// addbits(nnum_units_in_tick, 32); //num_units_in_tick
|
||||
// addbits(TIME_SCALE_IN_HZ, 32); //time_scale
|
||||
// addbits(0x1, 1); //fixed_frame_rate_flag
|
||||
|
||||
// addbits(0x0, 1); //nal_hrd_parameters_present_flag
|
||||
// addbits(0x0, 1); //vcl_hrd_parameters_present_flag
|
||||
// addbits(0x0, 1); //pic_struct_present_flag
|
||||
// addbits(0x0, 1); //bitstream_restriction_flag
|
||||
//END VUI
|
||||
|
||||
//BUG? addbits(0x0, 1); // frame_mbs_only_flag
|
||||
addbits(0x1, 1); // rbsp stop bit
|
||||
|
||||
dobytealign();
|
||||
}
|
||||
|
||||
//! Creates PPS NAL and add it to the output
|
||||
|
||||
//Creates and saves the NAL PPS (one per file)
|
||||
private void create_pps()
|
||||
{
|
||||
add4bytesnoemulationprevention(0x000001); // NAL header
|
||||
addbits(0x0, 1); // forbidden_bit
|
||||
addbits(0x3, 2); // nal_ref_idc
|
||||
addbits(0x8, 5); // nal_unit_type : 8 ( PPS )
|
||||
addexpgolombunsigned(0); // pic_parameter_set_id
|
||||
addexpgolombunsigned(0); // seq_parameter_set_id
|
||||
addbits(0x0, 1); // entropy_coding_mode_flag
|
||||
addbits(0x0, 1); // bottom_field_pic_order_in frame_present_flag
|
||||
addexpgolombunsigned(0); // nun_slices_groups_minus1
|
||||
addexpgolombunsigned(0); // num_ref_idx10_default_active_minus
|
||||
addexpgolombunsigned(0); // num_ref_idx11_default_active_minus
|
||||
addbits(0x0, 1); // weighted_pred_flag
|
||||
addbits(0x0, 2); // weighted_bipred_idc
|
||||
addexpgolombsigned(0); // pic_init_qp_minus26
|
||||
addexpgolombsigned(0); // pic_init_qs_minus26
|
||||
addexpgolombsigned(0); // chroma_qp_index_offset
|
||||
addbits(0x0, 1); //deblocking_filter_present_flag
|
||||
addbits(0x0, 1); // constrained_intra_pred_flag
|
||||
addbits(0x0, 1); //redundant_pic_ent_present_flag
|
||||
addbits(0x1, 1); // rbsp stop bit
|
||||
|
||||
dobytealign();
|
||||
}
|
||||
|
||||
//! Creates Slice NAL and add it to the output
|
||||
/*!
|
||||
\param lFrameNum number of frame
|
||||
*/
|
||||
|
||||
//Creates and saves the NAL SLICE (one per frame)
|
||||
//H264 Spec Section 7.3.3 Slice Header Syntax
|
||||
private void create_slice_header(uint lFrameNum)
|
||||
{
|
||||
add4bytesnoemulationprevention(0x000001); // NAL header
|
||||
addbits(0x0, 1); // forbidden_bit
|
||||
addbits(0x3, 2); // nal_ref_idc
|
||||
addbits(0x5, 5); // nal_unit_type : 5 ( Coded slice of an IDR picture )
|
||||
addexpgolombunsigned(0); // first_mb_in_slice
|
||||
addexpgolombunsigned(7); // slice_type
|
||||
addexpgolombunsigned(0); // pic_param_set_id
|
||||
|
||||
byte cFrameNum = 0; // (byte)(lFrameNum % 16); // H264 Spec says "If the current picture is an IDR picture, frame_num shall be equal to 0. "
|
||||
// Also any maths here must relate to the value of log2_max_frame_num_minus4 in the SPS
|
||||
|
||||
addbits(cFrameNum, 4); // frame_num ( numbits = v = log2_max_frame_num_minus4 + 4)
|
||||
|
||||
// idr_pic_id range is 0..65535. All slices in the same IDR must have the same pic_id. Spec says if there are two
|
||||
// IDRs back to back they must have different idr_pic_id values
|
||||
uint lidr_pic_id = lFrameNum % 65536;
|
||||
|
||||
addexpgolombunsigned(lidr_pic_id); // idr_pic_id
|
||||
|
||||
addbits(0x0, 4); // pic_order_cnt_lsb (numbits = v = log2_max_fpic_order_cnt_lsb_minus4 + 4)
|
||||
// nal_ref_idc != 0. Insert dec_ref_pic_marking
|
||||
addbits(0x0, 1); // no_output_of_prior_pics_flag
|
||||
addbits(0x0, 1); // long_term_reference_flag
|
||||
|
||||
addexpgolombsigned(0); //slice_qp_delta
|
||||
|
||||
//Probably NOT byte aligned!!!
|
||||
}
|
||||
|
||||
//! Creates macroblock header and add it to the output
|
||||
|
||||
//Creates and saves the macroblock header(one per macroblock)
|
||||
private void create_macroblock_header()
|
||||
{
|
||||
addexpgolombunsigned(25); // mb_type (I_PCM)
|
||||
}
|
||||
|
||||
//! Creates the slice footer and add it to the output
|
||||
|
||||
//Creates and saves the SLICE footer (one per SLICE)
|
||||
private void create_slice_footer()
|
||||
{
|
||||
addbits(0x1, 1); // rbsp stop bit
|
||||
}
|
||||
|
||||
//! Creates SPS NAL and add it to the output
|
||||
/*!
|
||||
\param nYpos First vertical macroblock pixel inside the frame
|
||||
\param nYpos nXpos horizontal macroblock pixel inside the frame
|
||||
*/
|
||||
|
||||
//Creates & saves a macroblock (coded INTRA 16x16)
|
||||
private void create_macroblock(uint nYpos, uint nXpos)
|
||||
{
|
||||
uint x;
|
||||
uint y;
|
||||
|
||||
create_macroblock_header();
|
||||
|
||||
dobytealign();
|
||||
|
||||
//Y
|
||||
uint nYsize = m_frame.nYwidth * m_frame.nYheight;
|
||||
for (y = nYpos * m_frame.nYmbheight; y < (nYpos + 1) * m_frame.nYmbheight; y++)
|
||||
{
|
||||
for (x = nXpos * m_frame.nYmbwidth; x < (nXpos + 1) * m_frame.nYmbwidth; x++)
|
||||
{
|
||||
addbyte(m_frame.yuv420pframe.pYCbCr[(y * m_frame.nYwidth + x)]);
|
||||
}
|
||||
}
|
||||
|
||||
//Cb
|
||||
uint nCsize = m_frame.nCwidth * m_frame.nCheight;
|
||||
for (y = nYpos * m_frame.nCmbheight; y < (nYpos + 1) * m_frame.nCmbheight; y++)
|
||||
{
|
||||
for (x = nXpos * m_frame.nCmbwidth; x < (nXpos + 1) * m_frame.nCmbwidth; x++)
|
||||
{
|
||||
addbyte(m_frame.yuv420pframe.pYCbCr[nYsize + (y * m_frame.nCwidth + x)]);
|
||||
}
|
||||
}
|
||||
|
||||
//Cr
|
||||
for (y = nYpos * m_frame.nCmbheight; y < (nYpos + 1) * m_frame.nCmbheight; y++)
|
||||
{
|
||||
for (x = nXpos * m_frame.nCmbwidth; x < (nXpos + 1) * m_frame.nCmbwidth; x++)
|
||||
{
|
||||
addbyte(m_frame.yuv420pframe.pYCbCr[nYsize + nCsize + (y * m_frame.nCwidth + x)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Constructor
|
||||
/*!
|
||||
\param pOutFile The output file pointer
|
||||
*/
|
||||
|
||||
//Private functions
|
||||
|
||||
//Contructor
|
||||
public CJOCh264encoder(List<byte> pOutFile) : base(pOutFile)
|
||||
{
|
||||
m_lNumFramesAdded = 0;
|
||||
|
||||
//C++ TO C# CONVERTER TODO TASK: The memory management function 'memset' has no equivalent in C#:
|
||||
//memset(m_frame, 0, sizeof(frame_t));
|
||||
m_nFps = 25;
|
||||
|
||||
m_pOutFile = pOutFile;
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
|
||||
//Destructor
|
||||
public override void Dispose()
|
||||
{
|
||||
free_video_src_frame();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
//! Initializes the coder
|
||||
/*!
|
||||
\param nImW Frame width in pixels
|
||||
\param nImH Frame height in pixels
|
||||
\param nFps Desired frames per second of the output file (typical values are: 25, 30, 50, etc)
|
||||
\param SampleFormat Sample format if the input file. In this implementation only SAMPLE_FORMAT_YUV420p is allowed
|
||||
\param nSARw Indicates the horizontal size of the sample aspect ratio (typical values are:1, 4, 16, etc)
|
||||
\param nSARh Indicates the vertical size of the sample aspect ratio (typical values are:1, 3, 9, etc)
|
||||
*/
|
||||
|
||||
//public functions
|
||||
|
||||
//Initilizes the h264 coder (mini-coder)
|
||||
public void IniCoder(uint nImW, uint nImH, uint nImFps, CJOCh264encoder.enSampleFormat SampleFormat, uint nSARw = 1, uint nSARh = 1)
|
||||
{
|
||||
m_lNumFramesAdded = 0;
|
||||
|
||||
if (SampleFormat != enSampleFormat.SAMPLE_FORMAT_YUV420p)
|
||||
{
|
||||
throw new System.Exception("Error: SAMPLE FORMAT not allowed. Only yuv420p is allowed in this version");
|
||||
}
|
||||
|
||||
free_video_src_frame();
|
||||
|
||||
//Ini vars
|
||||
m_frame.sampleformat = SampleFormat;
|
||||
m_frame.nYwidth = nImW;
|
||||
m_frame.nYheight = nImH;
|
||||
if (SampleFormat == enSampleFormat.SAMPLE_FORMAT_YUV420p)
|
||||
{
|
||||
//Set macroblock Y size
|
||||
m_frame.nYmbwidth = MACROBLOCK_Y_WIDTH;
|
||||
m_frame.nYmbheight = MACROBLOCK_Y_HEIGHT;
|
||||
|
||||
//Set macroblock C size (in YUV420 is 1/2 of Y)
|
||||
m_frame.nCmbwidth = MACROBLOCK_Y_WIDTH / 2;
|
||||
m_frame.nCmbheight = MACROBLOCK_Y_HEIGHT / 2;
|
||||
|
||||
//Set C size
|
||||
m_frame.nCwidth = m_frame.nYwidth / 2;
|
||||
m_frame.nCheight = m_frame.nYheight / 2;
|
||||
|
||||
//In this implementation only picture sizes multiples of macroblock size (16x16) are allowed
|
||||
if (((nImW % MACROBLOCK_Y_WIDTH) != 0) || ((nImH % MACROBLOCK_Y_HEIGHT) != 0))
|
||||
{
|
||||
throw new System.Exception("Error: size not allowed. Only multiples of macroblock are allowed (macroblock size is: 16x16)");
|
||||
}
|
||||
}
|
||||
m_nFps = nImFps;
|
||||
|
||||
//Alloc mem for 1 frame
|
||||
alloc_video_src_frame();
|
||||
|
||||
//Create h264 SPS & PPS
|
||||
create_sps(m_frame.nYwidth, m_frame.nYheight, m_frame.nYmbwidth, m_frame.nYmbheight, nImFps, nSARw, nSARh);
|
||||
close(); // Flush data to the List<byte>
|
||||
sps = m_pOutFile.ToArray();
|
||||
m_pOutFile.Clear();
|
||||
|
||||
create_pps();
|
||||
close(); // Flush data to the List<byte>
|
||||
pps = m_pOutFile.ToArray();
|
||||
m_pOutFile.Clear();
|
||||
}
|
||||
|
||||
//! Returns the frame pointer
|
||||
/*!
|
||||
\return Frame pointer ready to fill with frame pixels data (the format to fill the data is indicated by SampleFormat parameter when the coder is initialized
|
||||
*/
|
||||
|
||||
//Returns the frame pointer to load the video frame
|
||||
public byte[] GetFramePtr()
|
||||
{
|
||||
if (m_frame.yuv420pframe.pYCbCr == null)
|
||||
{
|
||||
throw new System.Exception("Error: video frame is null (not initialized)");
|
||||
}
|
||||
|
||||
return m_frame.yuv420pframe.pYCbCr;
|
||||
}
|
||||
|
||||
//! Returns the allocated frame memory in bytes
|
||||
/*!
|
||||
\return The allocated memory to store the frame data
|
||||
*/
|
||||
|
||||
//Returns the the allocated size for video frame
|
||||
public uint GetFrameSize()
|
||||
{
|
||||
return m_frame.nyuv420pframesize;
|
||||
}
|
||||
|
||||
//! It codes the frame that is in frame memory a it saves the coded data to disc
|
||||
|
||||
//Codifies & save the video frame (it only uses 16x16 intra PCM -> NO COMPRESSION!)
|
||||
public void CodeAndSaveFrame()
|
||||
{
|
||||
m_pOutFile.Clear();
|
||||
|
||||
//The slice header is not byte aligned, so the first macroblock header is not byte aligned
|
||||
create_slice_header(m_lNumFramesAdded);
|
||||
|
||||
//Loop over macroblock size
|
||||
uint y;
|
||||
uint x;
|
||||
for (y = 0; y < m_frame.nYheight / m_frame.nYmbheight; y++)
|
||||
{
|
||||
for (x = 0; x < m_frame.nYwidth / m_frame.nYmbwidth; x++)
|
||||
{
|
||||
create_macroblock(y, x);
|
||||
}
|
||||
}
|
||||
|
||||
create_slice_footer();
|
||||
dobytealign();
|
||||
|
||||
m_lNumFramesAdded++;
|
||||
|
||||
// flush
|
||||
close();
|
||||
nal = m_pOutFile.ToArray();
|
||||
}
|
||||
|
||||
//! Returns number of coded frames
|
||||
/*!
|
||||
\return The number of coded frames
|
||||
*/
|
||||
|
||||
//Returns the number of codified frames
|
||||
public uint GetSavedFrames()
|
||||
{
|
||||
return m_lNumFramesAdded;
|
||||
}
|
||||
|
||||
//! Flush all data and save the trailing bits
|
||||
|
||||
//Closes the h264 coder saving the last bits in the buffer
|
||||
public void CloseCoder()
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user