Linux 的魅力: 自动上传 Nokia N800 照片
来源:互联网 发布:网络证券销售怎么样 编辑:程序博客网 时间:2024/06/05 10:27
打印本页
将此页作为电子邮件发送
级别: 初级
Peter Seebach, 作家, 自由作家
2008 年 1 月 14 日
Linux 的魅力 的 3 期文章用实际例子演示了如何着手构建 Nokia N800 应用程序:使用摄像机功能创建 Webcam。本文是第 3 期,也是最后一期。本文将编写一个自动照片上传例程,用于上传所拍照片。
首先,让我们快速回顾一下。在这个分三部分的系列的 第 1 期 中,演示了 Nokia N800 Linux® 的内部结构,列出了它的 技术规范和物理参数,并阐述了如何设置和测试构建环境。在 第 2 期 的末尾,展示了一个程序,只要用户按下一个按钮,它就会将一幅图像压缩为 JPEG 文件,并将其保存在内存中。
现在,在第 3 期也是最后一期文章中,您将会看到如何将这些 JPEG 文件自动上传到远程站点。
上传文件
关于开发 Nokia N770 和 N800 的其他 developerWorks 文章- Linux 助力 Nokia 770
- 开发 Nokia N800
- 访问 Nokia N800 摄像机
- developerWorks 上所有的 Linux的魅力 系列文章
上传文件比我最初所希望的稍微困难一些。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
,提示文件传输没有全部完成。因为文件大小是我所期望的大小,并且不会影响到图像,所以我忽略了这个错误。
借助 libjpeg
和 libcurl
,摄像机应用程序基本上能够实现所需的功能了。它上传来自摄像机的图像流,因此不会消耗太多的本地磁盘空间。当然,如果很注重这项功能,可以添加配置选项,允许用户指定文件上传位置和上传方式。
您可能觉得用同一个缓冲区打开两个 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 开发的更全面讨论。
- Linux 的魅力: 自动上传 Nokia N800 照片
- Linux 的魅力: 自动上传 Nokia N800 照片
- Linux 的魅力: 开发 Nokia N800
- Linux 的魅力:访问 Nokia N800 摄像机
- Linux 的魅力: 开发 Nokia N800
- Linux 的魅力: Linux 助力 Nokia 770
- Linux 的魅力: Linux 助力 Nokia 770
- Nokia N800开发经验
- nokia n800 原来也可以跑php
- 关于照片上传,并给上传的文件自动命名(JSP+Sevrlet)
- Philips TEA5761 Radio Linux driver for N800
- 照片的批量上传JAVA
- 照片上传功能的实现
- springmvc的照片上传功能
- Linux魅力
- 初识linux和android的魅力
- 上传照片
- 照片上传
- 遥感影像和DEM免费下载网址
- PL/SQL语法
- Visual Studio 制作安装包的自动移除前版本的怪事
- Asp.net 利用Div固定表头+异步显示动态加载
- SQLServer和Oracle常用函数对比
- Linux 的魅力: 自动上传 Nokia N800 照片
- C/C++面试题大汇总『直接转载未作修改』
- 让oracle数据表不显示中文而变成问号的方法
- Oracle函数列表速查
- 双链表基本操作
- 连带目录结构复制文件的参数
- C++中类中static方法的使用,
- html动态显示提示
- javascript技巧(判断全角,查看字符的asc码,判断是否是数字)