Имя файла sample-Encoder-jpeg.c
Основной поток кода для получения изображений выглядит следующим образом:
Прежде всего, большинство функций нашего файла зависят от двух файлов sample-common.h и sample-common.c. Большинство демонстрационных программ разрабатываются путем вызова базовой библиотеки, предоставляемой common.c.
PS: Большинство основных библиотечных API-функций Ingenic T31 имеют формат IMP_XXX_XXX, например
IMP_OSD_SetPoolSize
IMP_ISP_AddSensor
и т. д.
int sample_system_init()
{
int ret = 0;
IMP_OSD_SetPoolSize(512*1024);
memset(&sensor_info, 0, sizeof(IMPSensorInfo));
memcpy(sensor_info.name, SENSOR_NAME, sizeof(SENSOR_NAME));
sensor_info.cbus_type = SENSOR_CUBS_TYPE;
memcpy(sensor_info.i2c.type, SENSOR_NAME, sizeof(SENSOR_NAME));
sensor_info.i2c.addr = SENSOR_I2C_ADDR;
IMP_LOG_DBG(TAG, "sample_system_init start\n");
ret = IMP_ISP_Open();
if(ret < 0){
IMP_LOG_ERR(TAG, "failed to open ISP\n");
return -1;
}
ret = IMP_ISP_AddSensor(&sensor_info);
if(ret < 0){
IMP_LOG_ERR(TAG, "failed to AddSensor\n");
return -1;
}
ret = IMP_ISP_EnableSensor();
if(ret < 0){
IMP_LOG_ERR(TAG, "failed to EnableSensor\n");
return -1;
}
ret = IMP_System_Init();
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_System_Init failed\n");
return -1;
}
/* enable turning, to debug graphics */
ret = IMP_ISP_EnableTuning();
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_ISP_EnableTuning failed\n");
return -1;
}
IMP_ISP_Tuning_SetContrast(128);
IMP_ISP_Tuning_SetSharpness(128);
IMP_ISP_Tuning_SetSaturation(128);
IMP_ISP_Tuning_SetBrightness(128);
#if 1
ret = IMP_ISP_Tuning_SetISPRunningMode(IMPISP_RUNNING_MODE_DAY);
if (ret < 0){
IMP_LOG_ERR(TAG, "failed to set running mode\n");
return -1;
}
#endif
#if 0
ret = IMP_ISP_Tuning_SetSensorFPS(SENSOR_FRAME_RATE_NUM, SENSOR_FRAME_RATE_DEN);
if (ret < 0){
IMP_LOG_ERR(TAG, "failed to set sensor fps\n");
return -1;
}
#endif
IMP_LOG_DBG(TAG, "ImpSystemInit success\n");
return 0;
}
В основном это объясняет эту функцию API: IMP_ISP_AddSensor.
Эта функция является функцией системы для загрузки датчиков. Наша программа T31 может загружать не только один датчик, но и множество различных датчиков. Среди них тестовая машина, которую я купил на Taobao, использует датчик типа SENSOR_SC5235.
На рисунке ниже мы можем наблюдать информацию о параметрах, передаваемую в основном в SDK через некоторые макроопределения датчика.
Датчиком по умолчанию в библиотеке SDK является jxf23, поэтому, если мы хотим вызвать поток кода JPEG для работы, нам нужно изменить код, прежде чем мы сможем его использовать.
Ключевые параметры функции IMP_FrameSource_CreateChn
Согласно тому, что мы говорили в предыдущей главе, если вам нужно использовать API-интерфейсы, связанные с видео, вы должны инициализировать источник видео. Шаги для инициализации источника видео:
Инициализация процесса инициализации
1. Создайте канал
2. Настройте каналы
3. Включить канал
Следующая функция в основном выполняет эти два шага:
Создайте каналы и установите свойства каналов.
основной поток:
struct chn_conf chn[FS_CHN_NUM] = {
{
.index = CH0_INDEX,
.enable = CHN0_EN,
.payloadType = IMP_ENC_PROFILE_HEVC_MAIN,
.fs_chn_attr = {
.pixFmt = PIX_FMT_NV12,
.outFrmRateNum = SENSOR_FRAME_RATE_NUM,
.outFrmRateDen = SENSOR_FRAME_RATE_DEN,
.nrVBs = 2,
.type = FS_PHY_CHANNEL,
.scaler.enable = 0,
.crop.enable = CROP_EN,
.crop.top = 0,
.crop.left = 0,
.crop.width = SENSOR_WIDTH,
.crop.height = SENSOR_HEIGHT,
.picWidth = SENSOR_WIDTH,
.picHeight = SENSOR_HEIGHT,
},
.framesource_chn = { DEV_ID_FS, CH0_INDEX, 0},
.imp_encoder = { DEV_ID_ENC, CH0_INDEX, 0},
},
int sample_framesource_init()
{
int i, ret;
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable) {
IMP_LOG_INFO(TAG,"sample_framesource_init i = %d\n",i);
ret = IMP_FrameSource_CreateChn(chn[i].index, &chn[i].fs_chn_attr);
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_FrameSource_CreateChn(chn%d) error !\n", chn[i].index);
return -1;
}
ret = IMP_FrameSource_SetChnAttr(chn[i].index, &chn[i].fs_chn_attr);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_FrameSource_SetChnAttr(chn%d) error !\n", chn[i].index);
return -1;
}
}
}
return 0;
}
Здесь мы задаем, сколько групп данных необходимо для кодирования, и концепцию настройки групп кодирования: об этом мы также говорили на предыдущем уроке;
Группа кодирования может не только кодировать данные кодового потока H264, но также кодировать данные JPEG.
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable) {
ret = IMP_Encoder_CreateGroup(chn[i].index);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_Encoder_CreateGroup(%d) error !\n", i);
return -1;
}
}
}
Самый важный параметр — это функция: IMP_Encoder_SetDefaultParam.
Установите параметры кодировки по умолчанию: IMP_ENC_PROFILE_JPEG.
int sample_jpeg_init()
{
int i, ret;
IMPEncoderChnAttr channel_attr;
IMPFSChnAttr *imp_chn_attr_tmp;
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable) {
imp_chn_attr_tmp = &chn[i].fs_chn_attr;
memset(&channel_attr, 0, sizeof(IMPEncoderChnAttr));
ret = IMP_Encoder_SetDefaultParam(&channel_attr, IMP_ENC_PROFILE_JPEG, IMP_ENC_RC_MODE_FIXQP,
imp_chn_attr_tmp->picWidth, imp_chn_attr_tmp->picHeight,
imp_chn_attr_tmp->outFrmRateNum, imp_chn_attr_tmp->outFrmRateDen, 0, 0, 25, 0);
/* Create Channel */
ret = IMP_Encoder_CreateChn(4 + chn[i].index, &channel_attr);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_Encoder_CreateChn(%d) error: %d\n",
chn[i].index, ret);
return -1;
}
/* Resigter Channel */
ret = IMP_Encoder_RegisterChn(i, 4 + chn[i].index);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_Encoder_RegisterChn(0, %d) error: %d\n",
chn[i].index, ret);
return -1;
}
}
}
return 0;
}
Основной шаг — связать вместе инициализированный источник видео и кодировщик.
/* Step.4 Bind */
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable) {
ret = IMP_System_Bind(&chn[i].framesource_chn, &chn[i].imp_encoder);
if (ret < 0) {
IMP_LOG_ERR(TAG, "Bind FrameSource channel%d and Encoder failed\n",i);
return -1;
}
}
}
Эта часть предназначена для того, чтобы сначала создать источник видео, снова настроить источник видео, а затем включить источник видео, как упоминалось ранее.
int sample_framesource_streamon()
{
int ret = 0, i = 0;
/* Enable channels */
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable) {
IMP_LOG_INFO(TAG,"sample_framesource_streamon i = %d\n",i);
ret = IMP_FrameSource_EnableChn(chn[i].index);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(%d) error: %d\n", ret, chn[i].index);
return -1;
}
}
}
return 0;
}
После того, как мы выполнили все действия, которые мы инициализировали ранее, включая инициализацию данных источника видео и привязку нашего метода кодирования, метод кодирования, который мы здесь связали, — это режим кодирования JPEG. Далее мы получим данные JPEG.
IMP_Encoder_StartRecvPic: открыть канал кодирования для получения изображений.
IMP_Encoder_PollingStream: кэш потока опроса, тайм-аут, который мы установили, составляет 1 секунду.
IMP_Encoder_GetStream:
Для кодовых потоков типов H264 и H265 один вызов успешно получает один кадр кодового потока. Этот кадр кодового потока может содержать несколько пакетов.
Для кодового потока типа JPEG один вызов успешно получает кодовый поток одного кадра. Этот кодовый поток кадра содержит только один пакет. Этот кадр содержит полную информацию файла изображения JPEG.
Наконец, мы используем эту функцию для обратного вызова данных структуры и сохранения их на нашей TF-карте, чтобы завершить этапы получения потока кода JPEG.
/**
* Определите структуру типа потока кадров кодирования
*/
typedef struct {
uint32_t phyAddr; /**< Физический адрес потока кода кадра */
uint32_t virAddr; /**< Виртуальный адрес пакета потока кадров */
uint32_t streamSize; /**< virAddr соответствует размеру выделенного адресного пространства. */
IMPEncoderPack *pack; /**< Структура пакета потока кадров */
uint32_t packCount; /**< Количество всех пакетов в одном кадре кодового потока */
uint32_t seq; /**< Порядковый номер потока закодированных кадров */
union
{
IMPEncoderStreamInfo streamInfo;
IMPEncoderJpegInfo jpegInfo;
};
} IMPEncoderStream;
static int save_stream(int fd, IMPEncoderStream *stream)
{
int ret, i, nr_pack = stream->packCount;
//IMP_LOG_DBG(TAG, "----------packCount=%d, stream->seq=%u start----------\n", stream->packCount, stream->seq);
for (i = 0; i < nr_pack; i++) {
//IMP_LOG_DBG(TAG, "[%d]:%10u,%10lld,%10u,%10u,%10u\n", i, stream->pack[i].length, stream->pack[i].timestamp, stream->pack[i].frameEnd, *((uint32_t *)(&stream->pack[i].nalType)), stream->pack[i].sliceType);
IMPEncoderPack *pack = &stream->pack[i];
if(pack->length){
uint32_t remSize = stream->streamSize - pack->offset;
if(remSize < pack->length){
ret = write(fd, (void *)(stream->virAddr + pack->offset), remSize);
if (ret != remSize) {
IMP_LOG_ERR(TAG, "stream write ret(%d) != pack[%d].remSize(%d) error:%s\n", ret, i, remSize, strerror(errno));
return -1;
}
ret = write(fd, (void *)stream->virAddr, pack->length - remSize);
if (ret != (pack->length - remSize)) {
IMP_LOG_ERR(TAG, "stream write ret(%d) != pack[%d].(length-remSize)(%d) error:%s\n", ret, i, (pack->length - remSize), strerror(errno));
return -1;
}
}else {
ret = write(fd, (void *)(stream->virAddr + pack->offset), pack->length);
if (ret != pack->length) {
IMP_LOG_ERR(TAG, "stream write ret(%d) != pack[%d].length(%d) error:%s\n", ret, i, pack->length, strerror(errno));
return -1;
}
}
}
}
//IMP_LOG_DBG(TAG, "----------packCount=%d, stream->seq=%u end----------\n", stream->packCount, stream->seq);
return 0;
}
IMP_Encoder_ReleaseStream
После того, как пользователь получит кодовый поток, он должен вовремя освободить кэш полученного кодового потока, иначе буфер кодового потока может переполниться и повлиять на кодирование кодера.
Этот интерфейс должен быть связан с IMP_Encoder_GetStream.
IMP_Encoder_StopRecvPic: прекратить кодирование каналов, получающих изображения.
sample_framesource_streamoff
int sample_framesource_streamoff()
{
int ret = 0, i = 0;
/* Enable channels */
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable){
ret = IMP_FrameSource_DisableChn(chn[i].index);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_FrameSource_DisableChn(%d) error: %d\n", ret, chn[i].index);
return -1;
}
}
}
return 0;
}
IMP_System_UnBind
Отменив привязку, мы просто связали поток, чтобы получить изображение. С этой точки зрения мы получаем только один кадр данных в основном потоке или подпотоке, поэтому в экспериментальной ситуации демо-версии также получены два изображения, одно из которых — изображение. основного потока и одно изображение подпотока.
int sample_jpeg_exit(void)
{
int ret = 0, i = 0, chnNum = 0;
IMPEncoderChnStat chn_stat;
for (i = 0; i < FS_CHN_NUM; i++) {
if (chn[i].enable) {
chnNum = 4 + chn[i].index;
memset(&chn_stat, 0, sizeof(IMPEncoderChnStat));
ret = IMP_Encoder_Query(chnNum, &chn_stat);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_Encoder_Query(%d) error: %d\n", chnNum, ret);
return -1;
}
if (chn_stat.registered) {
ret = IMP_Encoder_UnRegisterChn(chnNum);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_Encoder_UnRegisterChn(%d) error: %d\n", chnNum, ret);
return -1;
}
ret = IMP_Encoder_DestroyChn(chnNum);
if (ret < 0) {
IMP_LOG_ERR(TAG, "IMP_Encoder_DestroyChn(%d) error: %d\n", chnNum, ret);
return -1;
}
}
}
}
return 0;
}
sample_system_exit
int sample_system_exit()
{
int ret = 0;
IMP_LOG_DBG(TAG, "sample_system_exit start\n");
IMP_System_Exit();
ret = IMP_ISP_DisableSensor();
if(ret < 0){
IMP_LOG_ERR(TAG, "failed to EnableSensor\n");
return -1;
}
ret = IMP_ISP_DelSensor(&sensor_info);
if(ret < 0){
IMP_LOG_ERR(TAG, "failed to AddSensor\n");
return -1;
}
ret = IMP_ISP_DisableTuning();
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_ISP_DisableTuning failed\n");
return -1;
}
if(IMP_ISP_Close()){
IMP_LOG_ERR(TAG, "failed to open ISP\n");
return -1;
}
IMP_LOG_DBG(TAG, " sample_system_exit success\n");
return 0;
}
Здесь я намеренно добавил часть журнала в демо-версию, чтобы облегчить сравнение с ситуацией, о которой я упоминал выше. Реальная программа работает в соответствии с упомянутым мной процессом.
Полученное разрешение также соответствует заданному нами разрешению. Можно судить, что весь процесс завершился идеально.