ffmpeg学习准备之pgm格式文件的解析(封装版)

PGM格式文件解析的封装

  • 辅助函数
    • 说明
    • 代码
  • PGM结构体
  • PGM P2格式的函数
  • PGM P5格式的函数
  • PGM 两个格式之间的转换
  • PGM 格式调用层的封装
  • 辅助函数
  • 测试代码

辅助函数

说明

对前一篇写的函数进行封装和代码提炼

代码

peek函数(C语言是没有这个函数,自己写的,类似于C++里面的peek函数)

int peek(FILE *fp) {
	 // todo 判空未做
   // 保存当前文件指针的位置
    fpos_t pos;
    fgetpos(fp, &pos);
    int ch;
    // 读取文件指针当前指向的字符,文件指针的位置自动指向下一个位置
    // 类似于其它高级语言里面的迭代器
    ch = fgetc(fp);
    fsetpos(fp, &pos);
    return ch;
}

skip_comment函数(用来跳过换行和注释信息)

void skip_comment(FILE *fp) {
	 // 检测当前字符是注释开或者是换行,后面的判断是过滤多个空行
    while (peek(fp) == '#'||peek(fp) == '\n') {
    	  // 过滤注释信息,直到换行
				while (fgetc(fp) != '\n');
    }

}

swap_two_byte函数(用来交换双字节存放的值)

// 移位版
unsigned  short swap_two_byte(unsigned short data) {
    return ((0xff & data) << 8) | ((0xff00 & data) >> 8);
}
// 指针版
void swap_two_byte_pointer(unsigned short *ptr) {
    // 取出第一个字节数据
    unsigned char tmp = *((unsigned char *) ptr);
    *((unsigned char *) ptr) = *((unsigned char *) ptr + 1);
    *((unsigned char *) ptr + 1) = tmp;
}

PGM结构体

typedef struct {
    char model[3]; // 模式 P2和P5
    unsigned width; // 宽度
    unsigned height; // 高度
    unsigned max; // 最大值
    // 针对P5格式
    unsigned byte_num; // 字节数
    unsigned *data; // 数据(之前是二维数组,这里是直接用的一级指针;所谓的二维数组,实际在内存中也是一个连续一维数组)

} PGM_TypeDef;

PGM P2格式的函数

// C语言标准规定,当数组名作为数组定义的标识符(也就是定义或声明数组时)、sizeof 或 & 的操作数时,它才表示整个数组本身,在其他的表达式中,数组名会被转换为指向第 0 个元素的指针(地址)。
void pgm_p2_write(const PGM_TypeDef *pgm, const char *fileName) {
    FILE *fp = fopen(fileName, "w");
    if (fp == NULL) exit(-1);
    fprintf(fp, "%s\n", pgm->model);
    fprintf(fp, "%d %d\n", pgm->width, pgm->height);
    // max value
    fprintf(fp, "%d\n", pgm->max);
    // 数据
    for (int i = 0; i < pgm->width; ++i) {
        for (int j = 0; j < pgm->height; ++j) {
            fprintf(fp, "%d ", pgm->data[i + j]);
        }
        fputc('\n', fp);
    }
    fclose(fp);
}

PGM_TypeDef *pgm_p2_read(PGM_TypeDef *pgm, const char *fileName, FILE *fp) {

    // 开辟内存空间
    pgm->data = (unsigned *) malloc(sizeof(unsigned) * pgm->height * pgm->width);
    // 初始化数据
    memset(pgm->data, 0, sizeof(unsigned) * pgm->height * pgm->width);
    for (int i = 0; i < pgm->width; ++i) {
        for (int j = 0; j < pgm->height; ++j) {
            fscanf(fp, "%d", &pgm->data[i + j]);
        }
    }


    return pgm;
}

PGM P5格式的函数

void pgm_p5_write(const PGM_TypeDef *pgm, const char *fileName) {
    FILE *fp = fopen(fileName, "wb+");
    if (fp == NULL) exit(-1);
    fprintf(fp, "%s\n", pgm->model);
    fprintf(fp, "%d %d\n", pgm->width, pgm->height);
    fprintf(fp, "%d\n", pgm->max);
    for (int i = 0; i < pgm->width; ++i) {
        for (int j = 0; j < pgm->height; ++j) {
            unsigned value = (i + j) % pgm->max == 0 ? pgm->max : (i + j) % pgm->max;
            if (pgm->byte_num == 2) value = swap_two_byte(value);
            fwrite(&value, pgm->byte_num, 1, fp);
        }
    }
    fclose(fp);
}

PGM_TypeDef *pgm_p5_read(PGM_TypeDef *pgm, const char *fileName, FILE *fp) {
    skip_comment(fp);
    if (pgm->max > UCHAR_MAX) {
        pgm->byte_num = 2;
    } else {
        pgm->byte_num = 1;
    }
    pgm->data = (unsigned *) malloc(sizeof(unsigned) * pgm->height * pgm->width);
    for (int i = 0; i < pgm->width; ++i) {
        for (int j = 0; j < pgm->height; ++j) {
            unsigned data;
            fread(&data, pgm->byte_num, 1, fp);
            if (pgm->byte_num == 2) data = swap_two_byte(data); // 移位操作
            pgm->data[i + j] = data;
        }
    }
    return pgm;
}

PGM 两个格式之间的转换

void pgm_p2_to_p5_write(PGM_TypeDef *pgm, const char *pgm2_file, const char *pgm5_file) {
    pgm_read(pgm, pgm2_file);
    // P5 格式
    strcpy(pgm->model, "P5");
    pgm_write(pgm, pgm5_file);
}

void pgm_p5_to_p2_write(PGM_TypeDef *pgm, const char *pgm5_file, const char *pgm2_file) {
    pgm_read(pgm, pgm5_file);
    // P2 格式
    strcpy(pgm->model, "P2");
    pgm_write(pgm, pgm2_file);
}

PGM 格式调用层的封装

void pgm_write(PGM_TypeDef *pgm, const char *fileName) {
    if (strcmp(pgm->model, "P2") == 0) {
        pgm_p2_write(pgm, fileName);
    } else {
        pgm->byte_num = pgm->max > UCHAR_MAX ? 2 : 1;
        pgm_p5_write(pgm, fileName);
    }
}

PGM_TypeDef *pgm_read(PGM_TypeDef *pgm, const char *fileName) {
    // 模式只能选择 rb,只选r的话,会读取错误;应该是在只有r的情况下,window会自动在换行符那里进行处理
    FILE *fp = fopen(fileName, "rb");
//    fscanf(fp,"%s",pgm->model);
    fgets(pgm->model, 3, fp);
    // 跳过空格或注释
    skip_comment(fp);
    fscanf(fp, "%d %d", &pgm->width, &pgm->height);
    // 跳过空格或注释
    skip_comment(fp);
    fscanf(fp, "%d", &pgm->max);
    char default_mode[3] = {"P2"};
    // 跳过空格或注释
    skip_comment(fp);
    PGM_TypeDef *ptr;
    if (strcmp("P2", pgm->model) == 0) {
        ptr = pgm_p2_read(pgm, fileName, fp);
    } else {
        ptr = pgm_p5_read(pgm, fileName, fp);
    }
    fclose(fp);
    return ptr;
}

辅助函数

// 打印数据
void pgm_data_print(PGM_TypeDef *pgm) {
    for (int i = 0; i < pgm->width; ++i) {
        for (int j = 0; j < pgm->height; ++j) {
            printf("%d ", pgm->data[i + j]);
        }
        printf("\n");
    }
}
// 创建数据
void pgm_data_create(PGM_TypeDef *pgm) {
    pgm->data = (unsigned *) malloc(sizeof(unsigned) * pgm->width * pgm->height);
    for (int i = 0; i < pgm->width; ++i) {
        for (int j = 0; j < pgm->height; ++j) {
            pgm->data[i + j] = (i + j) % pgm->max == 0 ? pgm->max : (i + j) % pgm->max;
        }
    }
}
// 释放开辟的内存
void pgm_destroy(PGM_TypeDef *pgm) {
    if (pgm->data != NULL){
        free(pgm->data);
        pgm->data = NULL;
    }
}

测试代码

int main() {


    PGM_TypeDef pgm = {"P5", 200, 300, 257, 1};
    pgm_data_create(&pgm);
    pgm_write(&pgm, "test_p5.pgm");
    pgm_destroy(&pgm);

    PGM_TypeDef PGM_Read;
    pgm_read(&PGM_Read, "test_p5.pgm");
    pgm_data_print(&PGM_Read);
    free(PGM_Read.data);
//    PGM_TypeDef pgm;
//    pgm_p2_to_p5_write(&pgm, "test_p2.pgm", "test_p55.pgm");
//    pgm_p5_to_p2_write(&pgm, "test_p55.pgm", "test_p222.pgm");
//    pgm_data_print(&pgm);
    return 0;
}

你可能感兴趣的:(ffmpeg,C语言学习,音视频)