[Armadillo:07472] Re: Armadillo-420 MJPG-streamer の画像乱れ問題について
Shin-ya Koga
email@hidden
2011年 8月 13日 (土) 11:41:01 JST
サムシングプレシャスの古賀です。
小谷部さん([Armadillo:07470]):
>欠落したデータを調査しましたので、報告します。
>
>こちらでも中村様が書かれているように、イメージデータの
>先頭付近が1箇所のみ欠落していました(100〜200バイトくら
>い)。
>その他、フレームデータの途中から取り込まれたもの(先頭に
>SOIマーカーが存在しない)や途中までしか取り込まれていないも
>の(末尾にEOIマーカーが存在しない)もありますので、単にカメラ
>からのデータをタイミングによって取りこぼしているだけではないか
>と思います。
>
>しかし、データ転送量が少ない場合に欠落の症状が見られないのは
>気になります(部屋を暗くし全体がぼやけたような映像を撮影した
>場合やFPSの設定を極端に小さくした場合など)。
フレームデータ片の欠落が発生するかどうかの要因の一つが、データ
転送量である、という観測結果ですよね。また、データ片の欠落は、
MJPEG フレームの先頭だけでなく、途中や末尾でも発生している、と。
で、linux-2.6.26-at13 に入っている UVC ドライバのソースを追って
みたところ、アイソクロナス転送のパケット落ちを検出した際のエラー
回復処理を修正することにより、改善できそうに思えます。改善とい
うのは、[Armadillo:07466] で書いた、
古賀([Armadillo:07466]):
>今回の場合の、安直な対応としては、UVC ドライバに手を入れて、
>アイソクロナス転送のパケット取りこぼしを検出した場合は、その
>フレームを捨ててしまうようにすることでしょうね。あるいは、
という方策です。
以下に述べる修正個所は、以前に中村さんが [Armadillo:07440] で
報告していらしたログとソースを見比べて気付きました。
中村さん([Armadillo:07440]):
>昨日のsyslogデータを見直しててみたら、こんなのが残ってました。
>たぶん
> # echo 255 > /sys/module/uvcvideo/parameters/trace
>とやったときのログだと思います。
>
>Jul 28 13:38:29 192.168.0.52 armadillo440-0 kernel: uvcvideo: Dequeuing
buffer 1 (3, 97091 bytes).
>Jul 28 13:38:29 192.168.0.52 armadillo440-0 kernel:
>Jul 28 13:38:29 192.168.0.52 armadillo440-0 last message repeated 4 time(s)
ここで、
uvcvideo: USB isochronous frame lost (-18).
というメッセージは、uvc_video.c にある uvc_video_decode_isoc() で
出力しています。以下に、当該個所を引用します。
-------------------------ここから-------------------------
static void uvc_video_decode_isoc(struct urb *urb,
struct uvc_video_device *video, struct uvc_buffer *buf)
{
u8 *mem;
int ret, i;
for (i = 0; i < urb->number_of_packets; ++i) {
if (urb->iso_frame_desc[i].status < 0) {
uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
"lost (%d).\n", urb->iso_frame_desc[i].status);
continue; ★
}
…
}
-------------------------ここまで-------------------------
上の★の行では、アイソクロナス転送におけるパケット落ちを検出した
際、uvc_trace() でログ出力するだけで、パケット落ちの発生を記録す
ることは、していません。つまり、エラーの発生を無視しているわけで
す。
この部分と、他三ヶ所の実装を変更することにより、パケット落ちの
発生した MJPEG フレームを、UVC ドライバに捨てさせることができる
のではないかと思います。
以下に、必要と思われる変更箇所を記します。なお、以下に示した
ENABLE_DROP_INCOMPLETE_FRAME というコンパイルスイッチは、変更箇
所を分かりやすくするために勝手に作ったものです。目印として読んで
下さい。
(1)uvc_video.c の uvc_video_decode_isoc()
-------------------------ここから-------------------------
static void uvc_video_decode_isoc(struct urb *urb,
struct uvc_video_device *video, struct uvc_buffer *buf)
{
u8 *mem;
int ret, i;
for (i = 0; i < urb->number_of_packets; ++i) {
if (urb->iso_frame_desc[i].status < 0) {
uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
"lost (%d).\n", urb->iso_frame_desc[i].status);
#if ENABLE_DROP_INCOMPLETE_FRAME ★
buf->state = UVC_BUF_STATE_ERROR; // mark as eror frame
#endif
continue;
}
…
}
-------------------------ここまで-------------------------
(2)uvc_video.c の uvc_video_decode_start()
-------------------------ここから-------------------------
static int uvc_video_decode_start(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
…
if (fid != video->last_fid && buf->buf.bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
"toggled).\n");
#if ENABLE_DROP_INCOMPLETE_FRAME ★
if (buf->state != UVC_BUF_STATE_ERROR) {
buf->state = UVC_BUF_STATE_DONE;
}
#else
buf->state = UVC_BUF_STATE_DONE;
#endif
return -EAGAIN;
}
video->last_fid = fid;
return data[0];
}
-------------------------ここまで-------------------------
(3)uvc_video.c の uvc_video_decode_end()
-------------------------ここから-------------------------
static void uvc_video_decode_end(struct uvc_video_device *video,
struct uvc_buffer *buf, const __u8 *data, int len)
{
/* Mark the buffer as done if the EOF marker is set. */
if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
if (data[0] == len)
uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
#if ENABLE_DROP_INCOMPLETE_FRAME ★
if (buf->state != UVC_BUF_STATE_ERROR) {
buf->state = UVC_BUF_STATE_DONE;
}
#else
buf->state = UVC_BUF_STATE_DONE;
#endif
if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
video->last_fid ^= UVC_STREAM_FID;
}
}
-------------------------ここまで-------------------------
(5)uvc_queue.c の uvc_queue_next_buffer()
-------------------------ここから-------------------------
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf)
{
struct uvc_buffer *nextbuf;
unsigned long flags;
#if ENABLE_DROP_INCOMPLETE_FRAME ★
if (((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
buf->buf.length != buf->buf.bytesused))
|| (buf->state == UVC_BUF_STATE_ERROR)) {
#else
if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
buf->buf.length != buf->buf.bytesused) {
#endif
buf->state = UVC_BUF_STATE_QUEUED;
buf->buf.bytesused = 0;
return buf;
}
…
}
-------------------------ここまで-------------------------
上記の変更を加えることにより、アイソクロナス転送のパケット落ち
が発生したフレームデータは、UVC ヘッダの Frame ID が切り替わっ
た際(新しいフレームに切り替わった際)か、UVC ヘッダに EOF が
記録された際に、正常フレームをキューイングするタイミングで捨て
られると思います。
ところで、[Armadillo:07440] で中村さんが報告していらしたログの
うち、
>とか、
>
>Jul 28 13:38:32 192.168.0.52 armadillo440-0 kernel: uvcvideo: Dropping
payload (out of sync).
>Jul 28 13:38:32 192.168.0.52 armadillo440-0 last message repeated 89
time(s)
については、uvc_video_decode_start() で出していますが、このログ
が出るケースでは、アイソクロナス転送の受信パケットを捨ててエラー
快復するようになっていると思います。上記の変更を加えると、アイソ
クロナス転送のパケット落ちを検出した場合に、そのフレームに所属す
る後続のパケットが、このログを出している個所で全て捨てられるよう
になるでしょう。
以上、もし参考になりましたら幸いです。
--
古賀信哉 (株)サムシングプレシャス
armadillo メーリングリストの案内