1.1图片播放器

在此已完成对linux驱动的入门,写了个小程序以实践。在此感谢朱有鹏老师,学习了老师的课程才入门驱动。
博客以记录和理解为主,并不以讲解为目的,若有需要此源码,可在博客留下邮箱信息,或联系本人。

项目名称:图片播放器

项目介绍:

小项目支持显示bmp,png,JPEG图片,并通过点击触摸屏左右两端实现上下切换图片的效果。

主要技术:

  • linux Framebuffer驱动
  • linux input输入子系统
  • linux i2c子系统
  • 触摸屏驱动
  • libjpeg库
  • libpng库

主要模块功能:

1.驱动模块
2.目录扫描,图片管理模块
3.图片切换模块
4.图片显示模块

模块介绍与主要代码分析

驱动模块

小项目使用s5pv210开发板,linux平台开发。
在搭建开发板平台,完成对触摸屏,显示屏支持后,驱动模块主要的任务就是进行Framebuffer地址映射。

  • linux Framebuffer驱动
    参考此文章理解,移植framebufer驱动。
  • 触摸屏(gslX680)驱动
    参考此文章理解,移植gslX680驱动,内附源码下载。
  • 在此显示Framebuffer映射主要代码
int fb_driver(void)
{
    int fd = -1, ret = -1;


    struct fb_fix_screeninfo finfo = {0};
    struct fb_var_screeninfo vinfo = {0};

    //1步:打开设备
    fd = open(FBDEVICE, O_RDWR);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }
    printf("open %s success.\n", FBDEVICE);

    //2步:获取设备的硬件信息
    ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
    if (ret < 0)
    {
        perror("ioctl");
        return -1;
    }
    printf("smem_start = 0x%x, smem_len = %u.\n", finfo.smem_start, finfo.smem_len);//打印信息

    ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
    if (ret < 0)
    {
        perror("ioctl");
        return -1;
    }
        //打印信息
    printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres);
    printf("xres_virtual = %u, yres_virtual = %u.\n", vinfo.xres_virtual, vinfo.yres_virtual);
    printf("bpp = %u.\n", vinfo.bits_per_pixel);

    //3步:进行mmap
    unsigned long len = vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;
    printf("len = %ld\n", len);
    pfb = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (NULL == pfb)
    {
        perror("mmap");
        return -1;
    }
    printf("pfb = %p.\n", pfb);

    close(fd);

    return 0;
}

目录扫描,图片管理模块

目录扫描功能采用递归方法,扫描目录的所有文件,经过排除,最后只对普通文件做进一步处理。
图片管理采用数组来组织。经判后初始化数据类型。(预计是用链表实现的,可实际情况并不理想,源码中移植了linux内核链表,已验证可使用。理解可参考博客

//声明数组节点数据结构
typedef struct picture
{
    char name[PATHNAME_MAX];
    image_type_e type;  

}picture;
/*
*扫描目录,索引图片,并完成图片数据的初始化
*参数:目录的路径
*/
int scan_image(const char *path)
{
    // 在本函数中递归检索path文件夹
    //,将其中所有图片填充到iamges数组中去
    DIR *dir;
    struct dirent *ptr;
    char base[1000];
    struct stat sta;

    if ((dir = opendir(path)) == NULL)
    {
        perror("Open dir error...");
        exit(1);
    }

    // readdir函数每调用一次就会返回opendir打开的basepath目录下的一个文件,直到
    // basepath目录下所有文件都被读完之后,就会返回NULL
    while ((ptr = readdir(dir)) != NULL)
    {

        if(strcmp(ptr->d_name, ".")==0 || strcmp(ptr->d_name, "..")==0)   ///current dir OR parrent dir
            continue;

        // 用lstat来读取文件属性并判断文件类型
        memset(base,'\0',sizeof(base));
        strcpy(base,path);
        strcat(base,"/");
        strcat(base,ptr->d_name);
        lstat(base, &sta);

        if (S_ISREG(sta.st_mode))
        {
            // 如果是普通文件,就要在这里进行处理:
            // 处理思路就是 先判定是否属于已知的某种图片格式,如果是则放到images数组中
            // 如果都不属于则不理他
            if (!is_bmp(base))
            {
                images[image_index].type =IMAGE_TYPE_BMP;
                strcpy(images[image_index].name, base);
                //debug("sjk_test bmp:  \n");
            }
            else if (!is_jpg(base))
            {

                images[image_index].type =IMAGE_TYPE_JPG;
                strcpy(images[image_index].name, base);
                //debug("sjk_test jpg:  \n");
            }
            else if (!is_png(base))
            {
                images[image_index].type =IMAGE_TYPE_PNG;
                strcpy(images[image_index].name, base);
                //debug("sjk_test png:  \n");
            }       
            image_index++;                      
        }
        if (S_ISDIR(sta.st_mode))
        {
            //debug("directory.\n");
            scan_image(base);
        }
    }

}

图片切换模块

完成触摸屏驱动后,点击左右屏幕实现切换图片。

/*
*检测触摸屏,根据结果实现图片上下切换
*参数:无传参
*/
int ts_updown(void)
{
    // 第一步: 触摸屏的触摸操作检测
    int fd = -1, ret = -1;
    struct input_event ev;
    int i = 0;                  // 用来记录当前显示的是第几个图片
    int k =1;

    fd = open(DEVICE_TOUCHSCREEN, O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        return -1;
    }

    while (1)
    {
        memset(&ev, 0, sizeof(struct input_event));
        ret = read(fd, &ev, sizeof(struct input_event));
        if (ret != sizeof(struct input_event))
        {
            perror("read");
            close(fd);
            return -1;
        }

        // 第二步: 根据触摸坐标来翻页
        if ((ev.type == EV_ABS) && (ev.code == ABS_X))
        {
            // 确定这个是x坐标
            if ((ev.value >= 0) && (ev.value < TOUCH_WIDTH))
            {
                // 上翻页
                    debug("up     \n");                 
                    i--;
                    k =1;
                    if(i == -1){
                        i =image_index;
                    }

            }
            else if ((ev.value > (WIDTH - TOUCH_WIDTH)) && (ev.value <= WIDTH))
            {
                // 下翻页          
                    debug("down  .\n");                     
                    i++;
                    k=1;
                    if(i == (image_index)){
                        i =0;
                    }
            }
        }

        if(k == 1)
        {
            debug("show \n");
            show_image(i);
            k =0;
        }       
    }   
    close(fd);
    return 0;
}

/*
*根据图片类型,调用相应的函数显示图片
*参数:在图片数组中的数组编号
*/
int show_image(int i)
{
    //print_images();
    //debug("--------------------i = %d\n",i);
    printf("images[i].pathname = %s,        type = %d.\n", images[i].name, images[i].type);
    switch(images[i].type)
    {   

        case IMAGE_TYPE_BMP:
            debug("BMP \n");
            display_bmp(images[i].name);    
            break;

        case IMAGE_TYPE_JPG:
            debug("JPG  \n");
            display_jpg(images[i].name);
            break;

        case IMAGE_TYPE_PNG:
            debug("PNG  \n");
            display_png(images[i].name);    
            break;

        case IMAGE_TPPE_UNKNOWN:
            printf("unknow picture file \n");   
            break;
        default:
            break;
    }
}

图片显示模块

  • JPEG显示
    判断是否为JPEG图片, 通过判断特点位是否为JPEG专用字符,进而确认。
 /*
*测试是否为JPG图片
*参数:图片的路径
*返回值:是为0   否为1
*/
int is_jpg(const char *path)
{   
    FILE *file = NULL;
    char buf[2] = {0};
    // 打开文件
    file = fopen(path, "rb");
    if (NULL == file)
    {
        fprintf(stderr, "fopen %s error.\n", path);
        fclose(file);
        return -1;
    }
    // 读出前2个字节
    fread(buf, 1, 2, file);
    //debug("read: 0x%x%x\n", buf[0], buf[1]);
    // 判断是不是0xffd8
    if (!((buf[0] == 0xff) && (buf[1] == 0xd8)))
    {
        fclose(file);
    //  debug("%s is not a jpeg picture \n",path);
        return 1;       // 不是jpg图片
    }
    // 是0xffd8开头,就继续
    // 文件指针移动到倒数2个字符的位置
    fseek(file, -2, SEEK_END);
    // 读出2个字节
    fread(buf, 1, 2, file);
    //debug("read: 0x%x%x\n", buf[0], buf[1]);
    // 判断是不是0xffd9
    if (!((buf[0] == 0xff) && (buf[1] == 0xd9)))
    {       
        fclose(file);
        return 1;       // 不是jpg图片
    }
    fclose(file);

    return 0;
}

确认为JPEG图片后,显示图片。代码参考jpeglib里的example.c。
(有关错误处理方面和错误跳转不太理解,但通过空函数和空指针得以解决此问题)

struct my_error_mgr
{
  struct jpeg_error_mgr pub;    /* "public" fields */
 // jmp_buf setjmp_buffer;  /* for return to caller */

};

typedef struct my_error_mgr * my_error_ptr;

void my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
 // longjmp(myerr->setjmp_buffer, 1);
}

/*
*显示jpg图片
*参数:图片的路径
*/
int display_jpg (char * filename)
{

    struct jpeg_decompress_struct cinfo;

    struct my_error_mgr jerr;
    /* More stuff */
    FILE * infile;      /* source file */
    //JSAMPARRAY buffer;        /* Output row buffer */
    char * buffer =NULL;
    char * buffer1 =NULL;
    int row_stride;     /* physical row width in output buffer */

    int x = 0;
    int y =0;
    int cnt=0;
    int a =0;

    if ((infile = fopen(filename, "rb")) == NULL) 
    {
        fprintf(stderr, "can't open %s\n", filename);
        return 0;
    }

    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;

    jpeg_create_decompress(&cinfo);

    jpeg_stdio_src(&cinfo, infile);

    jpeg_read_header(&cinfo, TRUE);

    jpeg_start_decompress(&cinfo);

    row_stride = cinfo.output_width * cinfo.output_components;

    buffer = (char *)malloc(row_stride);
    buffer1 =  (char *)malloc(row_stride *cinfo.output_height);
    if(buffer1 ==NULL && buffer ==NULL)
        debug("buf after \n");
    while (cinfo.output_scanline < cinfo.output_height)
    {
        jpeg_read_scanlines(&cinfo, &buffer, 1);
        memcpy(buffer1 + (cinfo.output_scanline-1) * row_stride , buffer , row_stride);

    }

    for (y=0; y<cinfo.output_height ;y++)
    {
        for (x=0; x<cinfo.output_width; x++)
        { 
            cnt = cinfo.output_width * y + x;
            *(pfb + cnt) = ((buffer1[a+0]<<0) | (buffer1[a+1]<<8)| (buffer1[a+2]<<16)); 
            a += 3;
        }
    }

    jpeg_finish_decompress(&cinfo);

  /* Step 8: Release JPEG decompression object */
  /* This is an important step since it will release a good deal of memory. */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);

    return 1;
}
  • BMP显示
    通过判断头两个字符是否为”BM”,判断是否为BMP图片
/*
*测试是否为bmp图片
*参数:图片的路径
*返回值:是为0   否为1
*/
int is_bmp(const char *path)
{
    int fd = -1;
    unsigned char buf[2] = {0};
    ssize_t ret = 0;

    // 第一步: 打开bmp图片
    fd = open(path, O_RDONLY);
    if (fd < 0)
    {
        fprintf(stderr, "open %s error.\n", path);
        return -1;
    }

    // 第二步: 读取文件头信息
    ret = read(fd, buf, 2);
    if (ret !=  2)
    {
        fprintf(stderr, "read file header error.\n");
        ret = -1;
        goto close;
    }

    // 解析头
    // 第三步: 判断是不是BMP图片
    if ((buf[0] != 'B') || (buf[1] != 'M'))
    {
        //fprintf(stderr, "file %s is not a bmp picture.\n", path);
    //  printf("%s is not a bmp picture \n",path);
        ret = 1;
        goto close;
    }
    else
    {
        ret = 0;
    }

close:
    close(fd);
    return ret;
}

BMP本身没有对数据进行压缩,所以可直接读取数据进行操作。

char *max_sd = NULL;
extern unsigned int *pfb ;
/*
*显示bmp图片
*参数:图片的路径
*/
int display_bmp(const char *pathname)
{

    pic_info bmp_info;
    ClBitMapFileHeader bmp_FileHeader;
    ClBitMapInfoHeader bmp_InfoHeader

    unsigned int cnt = 0, a = 0;
    unsigned int x, y;

    int fd =-1;
    int ret =-1;
    char buf[2] = {0}; 

    bmp_info.pData = max_sd;

    fd = open(pathname, O_RDWR);
    if (fd < 0)
    {
        perror("open    \n");
        return -1;
    }
    printf("open %s success.\n", FBDEVICE);

    ret = read(fd ,buf ,2);
    if(ret == -1)
        {
            perror("read bmp_FileHeader \n");
        }
    if(buf[0] == "B" && buf[1] == "M" )
        {
            debug("it is not bmp picture \n");
            return -1;
        }
    ret = read(fd ,&bmp_FileHeader ,12);
    debug("bfSize = %ld.\n", bmp_FileHeader.bfSize);
    debug("bfOffBits = %ld.\n", bmp_FileHeader.bfOffBits);

    ret = read(fd ,&bmp_InfoHeader ,40);    
    if(ret == -1)
        {
            perror("read bmp_InfoHeader \n");
        }

    bmp_info.bpp = bmp_InfoHeader.biBitCount;
    bmp_info.height = bmp_InfoHeader.biHeight;
    bmp_info.width = bmp_InfoHeader.biWidth ;
    bmp_info.pathname = pathname;

    debug("bpp = %d height = %d width = %d \n" ,bmp_info.bpp,bmp_info.height,bmp_info.width);
    debug("PFB1 = %d\n",*pfb);
    ret =lseek(fd, bmp_FileHeader.bfOffBits, SEEK_SET);
    if(ret == -1)
        {
            perror("lseek   \n");
        }
    max_sd = (char *)malloc(bmp_info.bpp* bmp_info.height* bmp_info.width / 8);
    ret = read(fd , max_sd , bmp_info.bpp* bmp_info.height* bmp_info.width / 8); 
    for (y=0; y<bmp_info.height ;y++)
    {
        for (x=0; x<bmp_info.width; x++)
        { 
            cnt = bmp_info.width *bmp_info.height -bmp_info.width * y - x;
            *(pfb + cnt) = ((max_sd[a+0]<<0) | (max_sd[a+1]<<8)| (max_sd[a+2]<<16)); 
            a += 3;
        }
    }

    close(fd);
    return 1;   
}
  • 显示PNG图片
    通过调用libpng库函数png_sig_cmp,判断是否为PNG图片。
/*
*测试是否为png图片
*参数:图片的路径
*返回值:是为0   否为1
*/
int is_png(const char *path)
{
    FILE *fp = NULL;
    char buf[PNG_BYTES_TO_CHECK] = {0}; 

    /* Open the prospective PNG file. */   
    if ((fp = fopen(path, "rb")) == NULL)       
        return -1;  

    /* Read in some of the signature bytes */   
    if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK)       
        return -1; 

    /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.     
    Return nonzero (true) if they match */   

    //  png_sig_cmp return 0    if ture
    if(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK))
        {
        //  printf("%s is no a png picture ;\n", path);
            return 1;
        }

    return 0;
}

显示PNG图片,运用libpng库,完成PNG图片的显示。
可参考pnglib库里example.c或此博客

/*
*显示png图片
*参数:图片的路径
*/
int display_png(char* name)
{
     int i, j;
     int m_width, m_height;
     png_infop info_ptr;             //图片信息的结构体
     png_structp png_ptr;         //初始化结构体,初始生成,调用api时注意传入

     int a =0 ,cnt =0;

     FILE* file = fopen(name, "rb");    //打开的文件名
     printf("%s, %d\n", __FUNCTION__, __LINE__);

     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);   //创建初始化libpng库结构体
      info_ptr = png_create_info_struct(png_ptr);                        //创建图片信息结构体

     setjmp(png_jmpbuf(png_ptr));                              //设置错误的返回点

     // 这句很重要
     png_init_io(png_ptr, file);         //把文件加载到libpng库结构体中

     // 读文件了
     png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);        //读文件内容到info_ptr中


     // 得到文件的宽高色深
     if ((png_ptr != NULL) && (info_ptr != NULL))
     {
      m_width = png_get_image_width(png_ptr, info_ptr);             
      m_height = png_get_image_height(png_ptr, info_ptr);             //通过png库中的api获取图片的宽度和高度

      printf("%s, %d, m_width =%d, m_height = %d\n", __FUNCTION__, __LINE__, m_width, m_height);
     }
     int color_type = png_get_color_type(png_ptr, info_ptr);               //通过api获取color_type

     printf("%s, %d, color_type = %d\n", __FUNCTION__, __LINE__, color_type);


     int size = m_height * m_width * 4;

     unsigned char *bgra = NULL;

     bgra = malloc(size);
     if (NULL == bgra)
     {
      printf("%s, %d, bgra == NULL\n", __FUNCTION__, __LINE__);
      return 1;
 }
     int pos = 0;



 // row_pointers里边就是传说中的rgb数据了

    png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);

 // 拷贝!!注意,如果你读取的png没有A通道,就要3位3位的读。还有就是注意字节对其的问题,最简单的就是别用不能被4整除的宽度就行了。读过你实在想用,就要在这里加上相关的对齐处理。

     for(i = 0; i < m_height; i++)
     {
        for(j = 0; j < (4 * m_width); j += 4)
        {
         bgra[pos++] = row_pointers[i][j + 2]; // blue
         bgra[pos++] = row_pointers[i][j + 1]; // green
         bgra[pos++] = row_pointers[i][j];   // red
         bgra[pos++] = row_pointers[i][j + 3]; // alpha
        }
     }

    for (i=0; i<m_height ;i++)
    {
        for (j=0; j<m_width; j++)
        { 
            cnt = WIDTH * i + j;
            *(pfb + cnt) = ((bgra[a+0]<<0) | (bgra[a+1]<<8)| (bgra[a+2]<<16)| (bgra[a+3]<<24)); 
            a += 4;
        }
    }

    png_destroy_read_struct(&png_ptr, &info_ptr, 0);

    fclose(file);

    return 0;
}

至此,linux小项目 1.1图片播放器分析完成。

猜你喜欢

转载自blog.csdn.net/qq_38326800/article/details/82154729