diff options
author | Jean-François Moine <moinejf@free.fr> | 2011-02-10 05:38:58 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-03-21 19:32:06 -0400 |
commit | b96cfc33e74f5ee5a206b2d43bf0bc3898c5a572 (patch) | |
tree | fbd665d1a56c497de0effc449aef7c0ccfea59b8 | |
parent | dccdcccdd36bf7570967ff53411c7401b8e645d6 (diff) |
[media] gspca - sonixj: Update the JPEG quality for best image transfer
During image capture, the filling rate of the isoc packets is computed.
It is then used by a work queue to update the current JPEG quality.
Tested-by: Franck Bourdonnec <fbourdonnec@chez.com>
Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/gspca/sonixj.c | 109 |
1 files changed, 86 insertions, 23 deletions
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index d465022b8022..2bfb8df9168b 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c | |||
@@ -57,12 +57,18 @@ struct sd { | |||
57 | atomic_t avg_lum; | 57 | atomic_t avg_lum; |
58 | u32 exposure; | 58 | u32 exposure; |
59 | 59 | ||
60 | struct work_struct work; | ||
61 | struct workqueue_struct *work_thread; | ||
62 | |||
63 | u32 pktsz; /* (used by pkt_scan) */ | ||
64 | u16 npkt; | ||
65 | u8 nchg; | ||
60 | s8 short_mark; | 66 | s8 short_mark; |
61 | 67 | ||
62 | u8 quality; /* image quality */ | 68 | u8 quality; /* image quality */ |
63 | #define QUALITY_MIN 60 | 69 | #define QUALITY_MIN 25 |
64 | #define QUALITY_MAX 95 | 70 | #define QUALITY_MAX 90 |
65 | #define QUALITY_DEF 80 | 71 | #define QUALITY_DEF 70 |
66 | 72 | ||
67 | u8 reg01; | 73 | u8 reg01; |
68 | u8 reg17; | 74 | u8 reg17; |
@@ -100,6 +106,8 @@ enum sensors { | |||
100 | SENSOR_SP80708, | 106 | SENSOR_SP80708, |
101 | }; | 107 | }; |
102 | 108 | ||
109 | static void qual_upd(struct work_struct *work); | ||
110 | |||
103 | /* device flags */ | 111 | /* device flags */ |
104 | #define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */ | 112 | #define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */ |
105 | #define F_ILLUM 0x02 /* presence of illuminator */ | 113 | #define F_ILLUM 0x02 /* presence of illuminator */ |
@@ -1786,6 +1794,8 @@ static int sd_config(struct gspca_dev *gspca_dev, | |||
1786 | sd->ag_cnt = -1; | 1794 | sd->ag_cnt = -1; |
1787 | sd->quality = QUALITY_DEF; | 1795 | sd->quality = QUALITY_DEF; |
1788 | 1796 | ||
1797 | INIT_WORK(&sd->work, qual_upd); | ||
1798 | |||
1789 | return 0; | 1799 | return 0; |
1790 | } | 1800 | } |
1791 | 1801 | ||
@@ -2297,6 +2307,19 @@ static void setjpegqual(struct gspca_dev *gspca_dev) | |||
2297 | reg_w1(gspca_dev, 0x18, sd->reg18); | 2307 | reg_w1(gspca_dev, 0x18, sd->reg18); |
2298 | } | 2308 | } |
2299 | 2309 | ||
2310 | /* JPEG quality update */ | ||
2311 | /* This function is executed from a work queue. */ | ||
2312 | static void qual_upd(struct work_struct *work) | ||
2313 | { | ||
2314 | struct sd *sd = container_of(work, struct sd, work); | ||
2315 | struct gspca_dev *gspca_dev = &sd->gspca_dev; | ||
2316 | |||
2317 | mutex_lock(&gspca_dev->usb_lock); | ||
2318 | PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality); | ||
2319 | setjpegqual(gspca_dev); | ||
2320 | mutex_unlock(&gspca_dev->usb_lock); | ||
2321 | } | ||
2322 | |||
2300 | /* -- start the camera -- */ | 2323 | /* -- start the camera -- */ |
2301 | static int sd_start(struct gspca_dev *gspca_dev) | 2324 | static int sd_start(struct gspca_dev *gspca_dev) |
2302 | { | 2325 | { |
@@ -2610,6 +2633,11 @@ static int sd_start(struct gspca_dev *gspca_dev) | |||
2610 | setcolors(gspca_dev); | 2633 | setcolors(gspca_dev); |
2611 | setautogain(gspca_dev); | 2634 | setautogain(gspca_dev); |
2612 | setfreq(gspca_dev); | 2635 | setfreq(gspca_dev); |
2636 | |||
2637 | sd->pktsz = sd->npkt = 0; | ||
2638 | sd->nchg = sd->short_mark = 0; | ||
2639 | sd->work_thread = create_singlethread_workqueue(MODULE_NAME); | ||
2640 | |||
2613 | return gspca_dev->usb_err; | 2641 | return gspca_dev->usb_err; |
2614 | } | 2642 | } |
2615 | 2643 | ||
@@ -2686,6 +2714,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev) | |||
2686 | /* reg_w1(gspca_dev, 0xf1, 0x01); */ | 2714 | /* reg_w1(gspca_dev, 0xf1, 0x01); */ |
2687 | } | 2715 | } |
2688 | 2716 | ||
2717 | /* called on streamoff with alt==0 and on disconnect */ | ||
2718 | /* the usb_lock is held at entry - restore on exit */ | ||
2719 | static void sd_stop0(struct gspca_dev *gspca_dev) | ||
2720 | { | ||
2721 | struct sd *sd = (struct sd *) gspca_dev; | ||
2722 | |||
2723 | if (sd->work_thread != NULL) { | ||
2724 | mutex_unlock(&gspca_dev->usb_lock); | ||
2725 | destroy_workqueue(sd->work_thread); | ||
2726 | mutex_lock(&gspca_dev->usb_lock); | ||
2727 | sd->work_thread = NULL; | ||
2728 | } | ||
2729 | } | ||
2730 | |||
2689 | static void do_autogain(struct gspca_dev *gspca_dev) | 2731 | static void do_autogain(struct gspca_dev *gspca_dev) |
2690 | { | 2732 | { |
2691 | struct sd *sd = (struct sd *) gspca_dev; | 2733 | struct sd *sd = (struct sd *) gspca_dev; |
@@ -2778,7 +2820,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |||
2778 | int len) /* iso packet length */ | 2820 | int len) /* iso packet length */ |
2779 | { | 2821 | { |
2780 | struct sd *sd = (struct sd *) gspca_dev; | 2822 | struct sd *sd = (struct sd *) gspca_dev; |
2781 | int i; | 2823 | int i, new_qual; |
2782 | 2824 | ||
2783 | /* | 2825 | /* |
2784 | * A frame ends on the marker | 2826 | * A frame ends on the marker |
@@ -2818,6 +2860,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |||
2818 | data += i; | 2860 | data += i; |
2819 | } | 2861 | } |
2820 | 2862 | ||
2863 | /* count the packets and their size */ | ||
2864 | sd->npkt++; | ||
2865 | sd->pktsz += len; | ||
2866 | |||
2821 | /* search backwards if there is a marker in the packet */ | 2867 | /* search backwards if there is a marker in the packet */ |
2822 | for (i = len - 1; --i >= 0; ) { | 2868 | for (i = len - 1; --i >= 0; ) { |
2823 | if (data[i] != 0xff) { | 2869 | if (data[i] != 0xff) { |
@@ -2843,27 +2889,60 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |||
2843 | return; | 2889 | return; |
2844 | 2890 | ||
2845 | /* marker found */ | 2891 | /* marker found */ |
2846 | /* if some error, discard the frame */ | 2892 | /* if some error, discard the frame and decrease the quality */ |
2847 | marker_found: | 2893 | marker_found: |
2894 | new_qual = 0; | ||
2848 | if (i > 2) { | 2895 | if (i > 2) { |
2849 | if (data[i - 2] != 0xff || data[i - 1] != 0xd9) { | 2896 | if (data[i - 2] != 0xff || data[i - 1] != 0xd9) { |
2850 | gspca_dev->last_packet_type = DISCARD_PACKET; | 2897 | gspca_dev->last_packet_type = DISCARD_PACKET; |
2898 | new_qual = -3; | ||
2851 | } | 2899 | } |
2852 | } else if (i + 6 < len) { | 2900 | } else if (i + 6 < len) { |
2853 | if (data[i + 6] & 0x08) { | 2901 | if (data[i + 6] & 0x08) { |
2854 | gspca_dev->last_packet_type = DISCARD_PACKET; | 2902 | gspca_dev->last_packet_type = DISCARD_PACKET; |
2903 | new_qual = -5; | ||
2855 | } | 2904 | } |
2856 | } | 2905 | } |
2857 | 2906 | ||
2858 | gspca_frame_add(gspca_dev, LAST_PACKET, data, i); | 2907 | gspca_frame_add(gspca_dev, LAST_PACKET, data, i); |
2859 | 2908 | ||
2909 | /* compute the filling rate and a new JPEG quality */ | ||
2910 | if (new_qual == 0) { | ||
2911 | int r; | ||
2912 | |||
2913 | r = (sd->pktsz * 100) / | ||
2914 | (sd->npkt * | ||
2915 | gspca_dev->urb[0]->iso_frame_desc[0].length); | ||
2916 | if (r >= 85) | ||
2917 | new_qual = -3; | ||
2918 | else if (r < 75) | ||
2919 | new_qual = 2; | ||
2920 | } | ||
2921 | if (new_qual != 0) { | ||
2922 | sd->nchg += new_qual; | ||
2923 | if (sd->nchg < -6 || sd->nchg >= 12) { | ||
2924 | sd->nchg = 0; | ||
2925 | new_qual += sd->quality; | ||
2926 | if (new_qual < QUALITY_MIN) | ||
2927 | new_qual = QUALITY_MIN; | ||
2928 | else if (new_qual > QUALITY_MAX) | ||
2929 | new_qual = QUALITY_MAX; | ||
2930 | if (new_qual != sd->quality) { | ||
2931 | sd->quality = new_qual; | ||
2932 | queue_work(sd->work_thread, &sd->work); | ||
2933 | } | ||
2934 | } | ||
2935 | } else { | ||
2936 | sd->nchg = 0; | ||
2937 | } | ||
2938 | sd->pktsz = sd->npkt = 0; | ||
2939 | |||
2860 | /* if the marker is smaller than 62 bytes, | 2940 | /* if the marker is smaller than 62 bytes, |
2861 | * memorize the number of bytes to skip in the next packet */ | 2941 | * memorize the number of bytes to skip in the next packet */ |
2862 | if (i + 62 > len) { /* no more usable data */ | 2942 | if (i + 62 > len) { /* no more usable data */ |
2863 | sd->short_mark = i + 62 - len; | 2943 | sd->short_mark = i + 62 - len; |
2864 | return; | 2944 | return; |
2865 | } | 2945 | } |
2866 | |||
2867 | if (sd->ag_cnt >= 0) | 2946 | if (sd->ag_cnt >= 0) |
2868 | set_lum(sd, data + i); | 2947 | set_lum(sd, data + i); |
2869 | 2948 | ||
@@ -2878,22 +2957,6 @@ marker_found: | |||
2878 | } | 2957 | } |
2879 | } | 2958 | } |
2880 | 2959 | ||
2881 | static int sd_set_jcomp(struct gspca_dev *gspca_dev, | ||
2882 | struct v4l2_jpegcompression *jcomp) | ||
2883 | { | ||
2884 | struct sd *sd = (struct sd *) gspca_dev; | ||
2885 | |||
2886 | if (jcomp->quality < QUALITY_MIN) | ||
2887 | sd->quality = QUALITY_MIN; | ||
2888 | else if (jcomp->quality > QUALITY_MAX) | ||
2889 | sd->quality = QUALITY_MAX; | ||
2890 | else | ||
2891 | sd->quality = jcomp->quality; | ||
2892 | if (gspca_dev->streaming) | ||
2893 | setjpegqual(gspca_dev); | ||
2894 | return 0; | ||
2895 | } | ||
2896 | |||
2897 | static int sd_get_jcomp(struct gspca_dev *gspca_dev, | 2960 | static int sd_get_jcomp(struct gspca_dev *gspca_dev, |
2898 | struct v4l2_jpegcompression *jcomp) | 2961 | struct v4l2_jpegcompression *jcomp) |
2899 | { | 2962 | { |
@@ -2955,10 +3018,10 @@ static const struct sd_desc sd_desc = { | |||
2955 | .init = sd_init, | 3018 | .init = sd_init, |
2956 | .start = sd_start, | 3019 | .start = sd_start, |
2957 | .stopN = sd_stopN, | 3020 | .stopN = sd_stopN, |
3021 | .stop0 = sd_stop0, | ||
2958 | .pkt_scan = sd_pkt_scan, | 3022 | .pkt_scan = sd_pkt_scan, |
2959 | .dq_callback = do_autogain, | 3023 | .dq_callback = do_autogain, |
2960 | .get_jcomp = sd_get_jcomp, | 3024 | .get_jcomp = sd_get_jcomp, |
2961 | .set_jcomp = sd_set_jcomp, | ||
2962 | .querymenu = sd_querymenu, | 3025 | .querymenu = sd_querymenu, |
2963 | #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) | 3026 | #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) |
2964 | .int_pkt_scan = sd_int_pkt_scan, | 3027 | .int_pkt_scan = sd_int_pkt_scan, |