跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

Linux 的魅力: 自动上传 Nokia N800 照片

自由移动照片

Peter Seebach, 作家, 自由作家
Peter Seebach 收集在 Linux 上运行的小型设备。他讨厌将其制作成 Beowulf 集群。

简介:  Linux 的魅力 的 3 期文章用实际例子演示了如何着手构建 Nokia N800 应用程序:使用摄像机功能创建 Webcam。本文是第 3 期,也是最后一期。本文将编写一个自动照片上传例程,用于上传所拍照片。

查看本系列更多内容

发布日期: 2008 年 1 月 14 日
级别: 初级
访问情况 : 1768 次浏览
评论: 


首先,让我们快速回顾一下。在这个分三部分的系列的 第 1 期 中,演示了 Nokia N800 Linux® 的内部结构,列出了它的 技术规范和物理参数,并阐述了如何设置和测试构建环境。在 第 2 期 的末尾,展示了一个程序,只要用户按下一个按钮,它就会将一幅图像压缩为 JPEG 文件,并将其保存在内存中。

现在,在第 3 期也是最后一期文章中,您将会看到如何将这些 JPEG 文件自动上传到远程站点。

上传文件

关于开发 Nokia N770 和 N800 的其他 developerWorks 文章

上传文件比我最初所希望的稍微困难一些。N800 没有提供很多文件上传和下载工具(尽管它提供了 curl)。无论如何,应该避免将文件保存在本地。

此方法从应用程序直接使用 libcurl,而不是在命令行运行 curl。与 libjpeg 一样,Libcurl 用于处理 stdio FILE 对象,而不是内存缓冲区。

幸运的是,通过扩展 GNU C 库,可以改变这种现状。 fmemopen() 函数提供了一个 stdio FILE * 对象,该对象表示内存中的一个缓冲区。通过调用 fmemopen 取代 test.jpg 的 open,问题就解决一半了:


清单 1. 使用 fmemopen
                
static unsigned char jpegdata[JPEG_SIZE];
FILE *out;

out = fmemopen(jpegdata, JPEG_SIZE, "wb");

JPEG_SIZE 宏被定义为 512KB,通过试验可以达到这个值(在保证较高质量的前提下,我在测试中所见过的最大大小为 360KB 左右,因此我认为少量的安全冗余已经足够了)。

JPEG 库调用总体上没有什么变化。完成这些调用后,所需的只是使用 libcurl 上传文件。以下是上传文件的示例代码:


清单 2. 使用 libcurl
                
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE);
curl_easy_setopt(curl, CURLOPT_URL,
 "ftp://test:dwtest@www.example.net/public_html/test.jpeg");
curl_easy_setopt(curl, CURLOPT_READDATA, jpeg);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) size);
curl_easy_perform(curl);
curl_easy_cleanup(curl);

在大多数情况下,此代码都能发挥作用。我们忽略了 CURLOPT_INFILESIZE_LARGE 选项,它用于指定要上传的文件大小。 curl 上传整个文件 — 在 512KB 的情况下,大部分都是空字节。curl 库假定该大小是所建议的大小。幸运的是,我们可以再次使用 fmemopen 来解决:


清单 3. 再次使用 fmemopen
                
FILE *jpeg;
off_t size;
size = ftell(out);
jpeg = fmemopen(jpegdata, size, "rb");

现在,把新的 JPEG 文件句柄交给 curl,结果几乎与我所期望的一样。第一次以后的每次上传都会产生错误 18,CURLE_PARTIAL_FILE,提示文件传输没有全部完成。因为文件大小是我所期望的大小,并且不会影响到图像,所以我忽略了这个错误。

借助 libjpeglibcurl,摄像机应用程序基本上能够实现所需的功能了。它上传来自摄像机的图像流,因此不会消耗太多的本地磁盘空间。当然,如果很注重这项功能,可以添加配置选项,允许用户指定文件上传位置和上传方式。

您可能觉得用同一个缓冲区打开两个 FILE 对象会有风险。其实不用担心,因为两个对象重叠的部分并不会被执行。在 libcurl 开始之前,libjpeg 就已经完成对缓冲区的处理。fflush() 看起来有些多余,但是可以确保剩余的数据仍在内存中。当然,还有更多的工作要做。


图像排队

始终为同一个文件分配相同的名称并不是最佳方法,而且如果这样做,就得考虑一些问题,比如 “如果网络连接断开会发生什么”、“拍照的速度有多快”。初步的解决方案是,当后台线程试图上传编码消息时对这些消息进行存储。这会导致需要处理一些新的事情,特别是,针对上传进行排列的 JPEG 文件链接列表和一些 pthread 代码。

以下是第一个数据结构:


清单 4. JPEG 数据列表
                
typedef struct jpeg_data {
            struct jpeg_data *next, *prev;
            size_t size;
            unsigned char *data;
} jpeg_data_t;

传送的 AppData 结构需要一个指向 jpeg_data_t 的指针作为列表标题;还需要一个 pthread 互斥锁来确保在线程交互中上传例程和 JPEG 创建代码不会冲突。以下是具体的 JPEG 代码,从输出文件的刷新开始:


清单 5. 排队以便稍后传递
                
fflush(out);
j = malloc(sizeof(jpeg_data_t));
j->prev = NULL;
j->size = ftell(out);
j->data = malloc(j->size);
memcpy(j->data, jpeg_data, j->size);
pthread_mutex_lock(&appdata->jpeg_mutex);
j->next = appdata->jpegs;
if (j->next)
       j->next->prev = j;
appdata->jpegs = j;
pthread_mutex_unlock(&appdata->jpeg_mutex);
fclose(out);
jpeg_destroy_compress(&comp);

现在只剩下两个次要的任务 —— 初始化互斥锁和启动线程运行可上传的文件的列表。最后的任务可能是最有趣的代码块;此处删除了一些可预知但是冗长的代码块,以使代码更加简洁:


清单 6. 忙碌与等待
                
void *
upload_jpegs(void *v) {
  AppData *appdata = v;
  jpeg_data_t *j = NULL;
  for (;;) {
    pthread_mutex_lock(&appdata->jpeg_mutex);
    if (appdata->jpegs) {
      /* extract last item from the list, as j */
    }
    pthread_mutex_unlock(&appdata->jpeg_mutex);
    if (j) {
      if (upload_jpeg(j)) {
            free(j->data);
            free(j);
            j = NULL;
      } else {
            jpeg_data_t *list;
            pthread_mutex_lock(&appdata->jpeg_mutex);
            /* put j back on the list */
            pthread_mutex_unlock(&appdata->jpeg_mutex);
            sleep(10);
      }
    } else {
      if (appdata->done)
            pthread_exit(0);
      sleep(5);
    }
  }
}

检查 appdata->done 可以发现代码的另外一处需求;当 GUI 开始清除时,由于程序已经退出,用户无法知道是否还有图片等待上传,因此清除代码设置了一个标记,让上传程序知道没有需要处理的数据,然后使用 pthread_join() 等待上传程序。


打包

执行了诸多操作后(也借鉴了一些来自 #maemo IRC 频道的优秀技巧),摄像机应用程序现在能够正常运行了。下一步是将其打包,以便在 N800 上安装。标准方法是使用 dpkg 工具进行打包,这种方法不仅灵活而且功能强大;它也需要较大的开销,而且对于单个独立的二进制文件来说有些大材小用。

在本例中,我使用比较简单的策略,即构建一个非常小的包。一个 debian 包是一个包含 3 个文件的归档文件(通过 ar 命令生成):

  • 名为 debian-binary 的文件表示 debian 包版本(它只能包含文本 “2.0”),
  • 名为 control.tar.gz 的文件包含一些元数据,
  • 名为 data.tar.gz 的文件包含将要安装的文件。

数据文件事实上只需包含两个文件。一个是实际的程序,另一个为 “desktop” 文件,该文件告诉应用程序管理器关于数据文件的信息。我把实际的程序安装在 /usr/bin 中,把 desktop 文件安装在 /usr/share/applications/hildon 中。我使用了一个非常小的 desktop 文件:


清单 7. desktop 文件
                
[Desktop Entry]
Encoding=UTF-8
Version=0.1
Type=Application
Name=dW Cam
Exec=/usr/bin/dwcam
X-Window-Icon=tn-bookmarks-link
X-HildonDesk-ShowInToolbar=true
X-Osso-Type=application/x-executable
Terminal=false

该文件告诉应用程序管理器应该为应用程序创建一个条目,条目名为 dW Cam,该条目实际上是通过 /usr/bin/dwcam 来实现的。没有指定定制图标;系统只为应用程序使用默认的图标。可通过该图标在菜单上获取应用程序。将这两个文件编译为一个称作 data.tar.gz 的 tarball 文件;需要使用相对路径名进行编译,例如 dwcam 的路径名为 ./usr/bin/dwcam。

现在需要处理 debian 包文件元数据。该元数据至少包含一个版权文件、一个 changelog 和一个控制文件;它们将被编译为一个称为 control.tar.gz 的 tarball 文件。控制文件告诉 Debian 包代码如何处理应用程序。以下是这个控制文件。


清单 8. 控制文件
                
Package: cam-app
Section: user/accessories
Priority: optional
Maintainer: Peter Seebach <seebs@seebs.net>
Build-Depends: gstreamer (>= 0.10)
Stardards-Version: 3.6.0
Architecture: armel
Version: 0.1
Depends: libatk1.0-0 (>= 1.9.0), libc6 (>= 2.3.5-1), [...]
Description: A trivial camera application.
XB-Maemo-Icon-26:
 iVBORw0KGgoAAAANSUhEUgAAABoAAAAZCAAAAAAKtWG8AAAACXBIWXMAAAAnAAAAJwEqCZFP
 AAAAB3RJTUUH1QkMEgEBuF+MPAAAACF0RVh0Q29tbWVudABKUEVHOmdudS1oZWFkLmpwZyAy
 [...]

我并没有包含全部的依赖列表,但是通常情况下这应该是一个依赖关系列表。如果使用 dpkg 来构建包,这些依赖项将被自动填充。XB-Maemo-Icon-26 字段是一个小图标的 base64 表示。

一旦生成了控制和数据 tarball 文件,您需要使用 ar 将它们和 debian-binary 文件连接在一起。将 debian-binary 文件作为归档文件中的第一个文件,这一点很重要;否则,debian 工具不会将文件识别为一个 debian 包。连接完成之后,就可以安装生成的文件了,可以从命令行通过 dpkg -i dwcam-0.1.deb 命令安装,或者使用应用程序管理器,使用菜单项 Application -> Install from file... 安装。

快速检查表明,新图标已经出现在 Tools/Extras 菜单的 dW Cam 名称下面,而且应用程序能够正常运行。


改进空间

很容易发现,这个功能还有很大改进空间。如果需要移植到其他平台,也许有必要设置 dpkg 以处理不同版本。如果只有一个目标平台,那么可以将编译器的标记连接起来;如果需要多个目标平台,则有点困难。构建最终程序所使用的标记非常长:


清单 9. C 编译器标记
                
cc --std=c99 -g -O2 -Wall -I/usr/include/atk-1.0 \
                 -I/usr/include/gtk-2.0 -I/usr/include/pango-1.0 \
                 -I/usr/lib/gtk-2.0/include -I/usr/include/dbus-1.0 \
                 -I/usr/lib/dbus-1.0/include -I/usr/include/libxml2 \
                 -I/usr/lib/glib-2.0/include -I/usr/include/glib-2.0 \
                 -I/usr/include/gstreamer-0.10 -o dwcam dwcam.c \
                 -lgstbase-0.10 -lgstinterfaces-0.10 -losso -lgtk-x11-2.0 \
                 -ljpeg -lhildonbase -lhildonwidgets -lcurl

对于如何避免来自不同库的包含文件之间的冲突,不同系统具有不同的编程习惯;一些系统使用 -I/usr/local/include 处理上述的所有包含路径。尽管如此,虽然 dpkg 系统对于我的项目来说有些大材小用,如果想要继续开发 和扩展系统或其他设备,dpkg 系统非常有用而且速度很快。

显然,可以进行改进的另一个主要区域是二进制文件中硬编码密码的使用。应用程序应该有一个不错的用户界面,允许配置各种设置,比如如何动态地上传文件,上传到哪里;同样地,应用程序也应该可以设置在缺乏用户指示时拍照的频率。实现这个任务的代码并不很复杂;由于该技术与 N800 没有多大关系,也为了使代码更简洁,我们省略了一部分代码。这些代码更适合针对 Gtk 开发的更全面讨论。


参考资料

学习

获得产品和技术

  • GStreamer 是一个库,允许构造媒体处理组件图,包括简单 Ogg/Vorbis 回放和复杂音频(混合)和视频(非线性编辑)处理。

  • 订购 SEK for Linux,共包含两张 DVD,其中有用于 Linux 的最新 IBM 试用软件,包括 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。

  • 使用 IBM 试用软件,构建您的下一个 Linux 开发项目,可以直接从 developerWorks 下载。

讨论

关于作者

Peter Seebach

Peter Seebach 收集在 Linux 上运行的小型设备。他讨厌将其制作成 Beowulf 集群。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=281810
ArticleTitle=Linux 的魅力: 自动上传 Nokia N800 照片
publish-date=01142008
author1-email=developerworks@seebs.plethora.net
author1-email-cc=jaloi@us.ibm.com

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。