From fb315802b69cb2db2d5566e8901dfc078497e22e Mon Sep 17 00:00:00 2001 From: svcmobrel-release Date: Fri, 6 Sep 2024 00:00:37 -0700 Subject: [PATCH] Updating prebuilts and/or headers 22fd03618c218ae14ba59a5aab5f30b0acd2b6ea - libv4lconvert/Makefile 66dd7958319442bd52ba40ede28fbfe31bb4e074 - libv4lconvert/cpia1.c 3271d74d02e2f33c16e3e50aeb1268eb9c440782 - libv4lconvert/rgbyuv.c db3c69c666e451c5d4ef6d1b5a3117f4b128baa4 - libv4lconvert/libv4lconvert-priv.h 1f1d1e05443c4b824cd697c0ce5efa9ea1277964 - libv4lconvert/ov518-decomp.c b694b6348e035b3da880824c2c2768145c9b5199 - libv4lconvert/jpeg_memsrcdest.c 22a502c238e48f4b939c81de41feccfc7c366766 - libv4lconvert/Makefile.dGPU 60e29f95ea52df4407d771330897813cdb38340f - libv4lconvert/libv4lsyscall-priv.h cc3f3e94a21795990610e63887c30528bde7b42e - libv4lconvert/bayer.c 72953a5a3a56b0188d35f49246356b9c8c35756c - libv4lconvert/helper.c d6c1aba89bbcb6fef46a6f22b7ea01025435c44d - libv4lconvert/Makefile.am ddd39b2fe0e2a86a6c64031ccc0d36edfd9b0f1a - libv4lconvert/sn9c10x.c 07f8e7c84abfbbe76d49d8bfd1f4eae6ea39a90b - libv4lconvert/jpgl.c 84c9c3812d4b5f237c8cd616d37fc1161a212acc - libv4lconvert/se401.c fbbffd8182b4fe2e85289b6e784f70cba7ea7b1d - libv4lconvert/sq905c.c fa751ff0f78845f3b4591396710df3b165bc8d11 - libv4lconvert/mr97310a.c 6ad4947dca51a7e67e056561cdb445d6c354d23c - libv4lconvert/libv4lconvert.c 4eff5c1a5e0b99ce4d6e9aa63645d9628467fdc3 - libv4lconvert/sn9c2028-decomp.c 5430e46abb1ac7039ed0309ca338237533ff29c9 - libv4lconvert/sn9c20x.c 3c49d99b9753208a9c1c2a9c738a1e7ad291ca22 - libv4lconvert/jpeg_memsrcdest.h fb3344cfa8df97688332ee4fd3b17968437e8ad5 - libv4lconvert/helper-funcs.h 8b7644ac3d5c4161cfb6dcc2a34013f4c379c665 - libv4lconvert/libv4lconvert.export be9e3bf3d7d1086b6eed0c1bf2f574c2b1737c00 - libv4lconvert/tinyjpeg.c f08c902ecd48c2739956606b502fc0b8e4007703 - libv4lconvert/crop.c f4d73412805f12fa08dd79a43798a7f8d7acece9 - libv4lconvert/pac207.c 25130d299463897a09e8b9adf72389dac2e89fa4 - libv4lconvert/tinyjpeg-internal.h 463725aa4dd3fecaf89c0b8bbf4747f8f7577935 - libv4lconvert/jpeg.c 725c9b8d0bfadba566cf200921e602961cb12705 - libv4lconvert/spca561-decompress.c ff7444c48a8da88f8a466cfb138e30e585828cb3 - libv4lconvert/jl2005bcd.c 803c4d0b9364050eda163452b8792e62e221ab6d - libv4lconvert/tinyjpeg.h cc8982bb6f753249181c715fe6430ffefc78c23b - libv4lconvert/stv0680.c 3e8e6c1fb85e3c4b58c4e9b2b0a223ddc793edcb - libv4lconvert/libv4lconvert.pc.in 033894511bd7e8a374a52486889658faa17918c4 - libv4lconvert/flip.c f061a4e0e45ca8e0dbab630dd477e19a6c915fda - libv4lconvert/spca501.c b2c19c2eac71d39d3fb883cdc159a69c2afa8fd6 - libv4lconvert/ov511-decomp.c 1d9c446cd8a232da87bd79acebc93e018ec72499 - libv4lconvert/jidctflt.c 1949e23fe99ccd0a05dcd084848f6d38b0af7ab6 - libv4lconvert/hm12.c 7da402829dbff238ca6ac829c037a85476185db6 - libv4lconvert/processing/autogain.c a54c2cb0439e606af01d0b4f02704f411819d98c - libv4lconvert/processing/libv4lprocessing.c dae9c69b7f019d7d4494cd56e2cf757e8510824a - libv4lconvert/processing/whitebalance.c ebf12bcf99f35fb9c400b04a1439e68598268249 - libv4lconvert/processing/gamma.c 0390d660eb130f0e580832bcf8ad5069010d2696 - libv4lconvert/processing/libv4lprocessing.h 33ab91b54108e8c24cbb80c5c335d96391d440b2 - libv4lconvert/processing/libv4lprocessing-priv.h 19a7fd04cdeba61172f281806d030472dee79fcd - libv4lconvert/control/libv4lcontrol.c 1e08fb01a598d71e3fc69656c4f2291f7dc13105 - libv4lconvert/control/libv4lcontrol.h 70f4992835e964b2698473971904375333e3659b - libv4lconvert/control/libv4lcontrol-priv.h 6feb5b2b8c99c99712dd1ea7fe9ab674d58bf86b - include/libv4l1.h bc44111fd6b2f0374a9fc67b1b23666c5c498b2c - include/libv4l2rds.h f751b481c4a9203345cdbb6459d0f2882f7cdbd9 - include/libv4lconvert.h 1edc439e6c0fc98513fa4a69557eb6221d043be0 - include/libv4l2.h f2b73fa5ab10ea7038e58bd9a4461d8e16316249 - include/libv4l1-videodev.h 94434b9692371b7d5f54ddef2141d22d90079ce9 - include/libv4l-plugin.h c84a9a115a21d1fd20da0f6ca3df7b46dd23cd2a - include/config.h 1ba874a7cad36ff31e4af3bfb37b98c05063d6b2 - include/libdvbv5/desc_event_extended.h 6bd2ed0beaf6aa4838e239198564fd8e1d20a3a1 - include/libdvbv5/desc_t2_delivery.h 92d4c28148d0b537c8afc289e1a76de68435cba0 - include/libdvbv5/dvb-scan.h 22c83d133e5c1d2648efb3028e0d89c970d0aad4 - include/libdvbv5/desc_partial_reception.h 98365b48442b9e3abb58101983b5da8c14f78289 - include/libdvbv5/dvb-v5-std.h 2f55ba765c689500401111747bb381b5aca77b30 - include/libdvbv5/desc_ca.h 100c02ce3bc364ddff895c75f4fb1f928a748d2d - include/libdvbv5/desc_cable_delivery.h 96db22ef84892a36d5df3cffa0b30d5bad01939c - include/libdvbv5/desc_logical_channel.h 7645dda247bcd45628afbb74ec2707a47050992e - include/libdvbv5/nit.h 30e9a7240938943de2725f2b335b19ad320179a5 - include/libdvbv5/header.h c1212a9308d96730de547648d3cda2fc144d0e29 - include/libdvbv5/desc_atsc_service_location.h 5b4a5e7fb30a7f28118be012837e73a7151d2619 - include/libdvbv5/cat.h 2560f18846a535a2c02e1ae449511e731f11c011 - include/libdvbv5/desc_ca_identifier.h 450fab787e61210c0c5f527df92c31c90b44a113 - include/libdvbv5/desc_service.h cabecc6d7c9fdf1c437273bd6a746bf83c156f72 - include/libdvbv5/desc_frequency_list.h 7a6093b13354d054cac78ea118a96e813cac3395 - include/libdvbv5/atsc_eit.h 9a2b20076d6728b5799096e4149e33a73119e1ef - include/libdvbv5/desc_sat.h 146f4f53fc49c66b59905249c0142efffd72fc54 - include/libdvbv5/desc_network_name.h efa3a711499f68ae370d49d98dc1963bf6bafcd8 - include/libdvbv5/desc_extension.h 44ab16a8d4eae09690c71a6301927c1da55dda6d - include/libdvbv5/descriptors.h c18291ff9009bfe71a2c7c6f0fce75331dc95e30 - include/libdvbv5/sdt.h 5e2dfc1d9a71805389e9a7932812695d0309050c - include/libdvbv5/dvb-frontend.h e81b7f75c11f175cf365fc7fb535e80828f10e24 - include/libdvbv5/dvb-file.h d7a096d51e3050c8f52e0e2111d88b71a5313da1 - include/libdvbv5/dvb-demux.h bdf514383ca0afe981cf4fd6af86440db2dc6667 - include/libdvbv5/pat.h b72b6d1ffcdd81e3e631c7c20bb30e5c287dc7ff - include/libdvbv5/vct.h 9b5cfad4a5f41cbf886507da6e79b07314827b32 - include/libdvbv5/desc_language.h 6e6fd4c61c1f61006c63214cbe4868d49428ddb9 - include/libdvbv5/mpeg_pes.h 188fc2cbec97288787a7f66554a4b6288224f980 - include/libdvbv5/desc_isdbt_delivery.h 73b7b0cf684de0e8a4eae49a8521f81b411d7b72 - include/libdvbv5/desc_ts_info.h 7544b5fb8f621a9c637c40d8f7a2a71f6ab4bd63 - include/libdvbv5/desc_hierarchy.h 9d523ee179af955a687662996050ee3cfaacf2ab - include/libdvbv5/crc32.h 7fb0966c6a1ccdf1a8844aed4a94d4ae1d02fcd7 - include/libdvbv5/dvb-fe.h c8b4fc511833f0993fa740a529e1f61e0f5a216f - include/libdvbv5/mpeg_es.h ac87e3306569dae329809f27ef227c5d50f0b60e - include/libdvbv5/desc_event_short.h 40a06b5375dbc0de88a15d26cc6c1e9a505119bc - include/libdvbv5/eit.h ef979f3276cc3cad6e947865a42643fbba860c69 - include/libdvbv5/mgt.h b867a2e7941d718aa64b2f6a1402322b616cb2da - include/libdvbv5/pmt.h fb8d640da36b6a156cbe0ef12dc25468de89a2a1 - include/libdvbv5/dvb-sat.h 4c412880f0c49cd00cb16e56eed082c4744211a5 - include/libdvbv5/countries.h ad13bfa0b1642fc72cca387e62bc193974c8d5ee - include/libdvbv5/atsc_header.h 02168c58e3c772f116f075085579ac4a8422e819 - include/libdvbv5/desc_terrestrial_delivery.h 4fe7def34ff640fc5e327b3596298169fdfe2f1c - include/libdvbv5/mpeg_ts.h d562371bb8a3b961c4d63a0f5618453bdff4bcd3 - include/libdvbv5/dvb-log.h 2542aabb7fbff4b1a09faaadec6006c4410a6d10 - libv4l2/libv4l2-priv.h 4ba98a607592ed0b8327b387af354544c65c9b67 - libv4l2/v4l2-plugin-android.c 752cb342c44989a8c172e3280e220a6fa2ec86b5 - libv4l2/Makefile ffecae84262f548deac1da0fa51f1aba6b6f96a0 - libv4l2/Makefile.dGPU 766aaca553b0166eb736557e44ad42b69464aa53 - libv4l2/libv4l2.export e6516370c43e4869e05a540d2e4ef584ac64890a - libv4l2/v4l2-plugin.c 8e335567bf404eeb3d180dd384309f687f2ab944 - libv4l2/Makefile.am d1f2b6f016cfb90c616d848418feb915e3737fa7 - libv4l2/libv4l2.c 7fa618184ff89737d13164be0b79e227d81f398c - libv4l2/log.c cbcee4426c19c168c6f49d04af3a0b2e30c0b681 - libv4l2/libv4l2.pc.in 9d456d1772885d900865a8958c0291e13d509de5 - libv4l2/v4l2convert.c Change-Id: Ib9c2be8c51af3ac26ffd9f230946bf7bff65a02b --- commitFile.txt | 110 + include/config.h | 220 ++ include/libdvbv5/atsc_eit.h | 221 ++ include/libdvbv5/atsc_header.h | 60 + include/libdvbv5/cat.h | 107 + include/libdvbv5/countries.h | 869 ++++++ include/libdvbv5/crc32.h | 59 + include/libdvbv5/desc_atsc_service_location.h | 141 + include/libdvbv5/desc_ca.h | 126 + include/libdvbv5/desc_ca_identifier.h | 115 + include/libdvbv5/desc_cable_delivery.h | 133 + include/libdvbv5/desc_event_extended.h | 126 + include/libdvbv5/desc_event_short.h | 119 + include/libdvbv5/desc_extension.h | 225 ++ include/libdvbv5/desc_frequency_list.h | 113 + include/libdvbv5/desc_hierarchy.h | 115 + include/libdvbv5/desc_isdbt_delivery.h | 135 + include/libdvbv5/desc_language.h | 104 + include/libdvbv5/desc_logical_channel.h | 133 + include/libdvbv5/desc_network_name.h | 113 + include/libdvbv5/desc_partial_reception.h | 126 + include/libdvbv5/desc_sat.h | 148 + include/libdvbv5/desc_service.h | 119 + include/libdvbv5/desc_t2_delivery.h | 169 ++ include/libdvbv5/desc_terrestrial_delivery.h | 161 + include/libdvbv5/desc_ts_info.h | 145 + include/libdvbv5/descriptors.h | 772 +++++ include/libdvbv5/dvb-demux.h | 142 + include/libdvbv5/dvb-fe.h | 771 +++++ include/libdvbv5/dvb-file.h | 514 ++++ include/libdvbv5/dvb-frontend.h | 592 ++++ include/libdvbv5/dvb-log.h | 111 + include/libdvbv5/dvb-sat.h | 151 + include/libdvbv5/dvb-scan.h | 419 +++ include/libdvbv5/dvb-v5-std.h | 268 ++ include/libdvbv5/eit.h | 224 ++ include/libdvbv5/header.h | 140 + include/libdvbv5/mgt.h | 187 ++ include/libdvbv5/mpeg_es.h | 249 ++ include/libdvbv5/mpeg_pes.h | 249 ++ include/libdvbv5/mpeg_ts.h | 173 ++ include/libdvbv5/nit.h | 276 ++ include/libdvbv5/pat.h | 172 ++ include/libdvbv5/pmt.h | 289 ++ include/libdvbv5/sdt.h | 187 ++ include/libdvbv5/vct.h | 251 ++ include/libv4l-plugin.h | 45 + include/libv4l1-videodev.h | 207 ++ include/libv4l1.h | 74 + include/libv4l2.h | 115 + include/libv4l2rds.h | 351 +++ include/libv4lconvert.h | 156 + libv4l2/Makefile | 61 + libv4l2/Makefile.am | 28 + libv4l2/Makefile.dGPU | 36 + libv4l2/libv4l2-priv.h | 118 + libv4l2/libv4l2.c | 1781 +++++++++++ libv4l2/libv4l2.export | 13 + libv4l2/libv4l2.pc.in | 12 + libv4l2/log.c | 259 ++ libv4l2/v4l2-plugin-android.c | 151 + libv4l2/v4l2-plugin.c | 120 + libv4l2/v4l2convert.c | 167 ++ libv4lconvert/Makefile | 67 + libv4lconvert/Makefile.am | 31 + libv4lconvert/Makefile.dGPU | 41 + libv4lconvert/bayer.c | 632 ++++ libv4lconvert/control/libv4lcontrol-priv.h | 79 + libv4lconvert/control/libv4lcontrol.c | 1120 +++++++ libv4lconvert/control/libv4lcontrol.h | 78 + libv4lconvert/cpia1.c | 213 ++ libv4lconvert/crop.c | 287 ++ libv4lconvert/flip.c | 266 ++ libv4lconvert/helper-funcs.h | 79 + libv4lconvert/helper.c | 219 ++ libv4lconvert/hm12.c | 158 + libv4lconvert/jidctflt.c | 284 ++ libv4lconvert/jl2005bcd.c | 219 ++ libv4lconvert/jpeg.c | 428 +++ libv4lconvert/jpeg_memsrcdest.c | 313 ++ libv4lconvert/jpeg_memsrcdest.h | 13 + libv4lconvert/jpgl.c | 727 +++++ libv4lconvert/libv4lconvert-priv.h | 283 ++ libv4lconvert/libv4lconvert.c | 1701 +++++++++++ libv4lconvert/libv4lconvert.export | 23 + libv4lconvert/libv4lconvert.pc.in | 11 + libv4lconvert/libv4lsyscall-priv.h | 129 + libv4lconvert/mr97310a.c | 208 ++ libv4lconvert/ov511-decomp.c | 666 ++++ libv4lconvert/ov518-decomp.c | 1480 +++++++++ libv4lconvert/pac207.c | 436 +++ libv4lconvert/processing/autogain.c | 217 ++ libv4lconvert/processing/gamma.c | 61 + .../processing/libv4lprocessing-priv.h | 67 + libv4lconvert/processing/libv4lprocessing.c | 187 ++ libv4lconvert/processing/libv4lprocessing.h | 42 + libv4lconvert/processing/whitebalance.c | 209 ++ libv4lconvert/rgbyuv.c | 756 +++++ libv4lconvert/se401.c | 164 + libv4lconvert/sn9c10x.c | 273 ++ libv4lconvert/sn9c2028-decomp.c | 154 + libv4lconvert/sn9c20x.c | 128 + libv4lconvert/spca501.c | 250 ++ libv4lconvert/spca561-decompress.c | 1006 +++++++ libv4lconvert/sq905c.c | 217 ++ libv4lconvert/stv0680.c | 40 + libv4lconvert/tinyjpeg-internal.h | 127 + libv4lconvert/tinyjpeg.c | 2670 +++++++++++++++++ libv4lconvert/tinyjpeg.h | 76 + push_info.txt | 1 + v4l2libs_README.txt | 31 + 111 files changed, 30910 insertions(+) create mode 100644 commitFile.txt create mode 100644 include/config.h create mode 100644 include/libdvbv5/atsc_eit.h create mode 100644 include/libdvbv5/atsc_header.h create mode 100644 include/libdvbv5/cat.h create mode 100644 include/libdvbv5/countries.h create mode 100644 include/libdvbv5/crc32.h create mode 100644 include/libdvbv5/desc_atsc_service_location.h create mode 100644 include/libdvbv5/desc_ca.h create mode 100644 include/libdvbv5/desc_ca_identifier.h create mode 100644 include/libdvbv5/desc_cable_delivery.h create mode 100644 include/libdvbv5/desc_event_extended.h create mode 100644 include/libdvbv5/desc_event_short.h create mode 100644 include/libdvbv5/desc_extension.h create mode 100644 include/libdvbv5/desc_frequency_list.h create mode 100644 include/libdvbv5/desc_hierarchy.h create mode 100644 include/libdvbv5/desc_isdbt_delivery.h create mode 100644 include/libdvbv5/desc_language.h create mode 100644 include/libdvbv5/desc_logical_channel.h create mode 100644 include/libdvbv5/desc_network_name.h create mode 100644 include/libdvbv5/desc_partial_reception.h create mode 100644 include/libdvbv5/desc_sat.h create mode 100644 include/libdvbv5/desc_service.h create mode 100644 include/libdvbv5/desc_t2_delivery.h create mode 100644 include/libdvbv5/desc_terrestrial_delivery.h create mode 100644 include/libdvbv5/desc_ts_info.h create mode 100644 include/libdvbv5/descriptors.h create mode 100644 include/libdvbv5/dvb-demux.h create mode 100644 include/libdvbv5/dvb-fe.h create mode 100644 include/libdvbv5/dvb-file.h create mode 100644 include/libdvbv5/dvb-frontend.h create mode 100644 include/libdvbv5/dvb-log.h create mode 100644 include/libdvbv5/dvb-sat.h create mode 100644 include/libdvbv5/dvb-scan.h create mode 100644 include/libdvbv5/dvb-v5-std.h create mode 100644 include/libdvbv5/eit.h create mode 100644 include/libdvbv5/header.h create mode 100644 include/libdvbv5/mgt.h create mode 100644 include/libdvbv5/mpeg_es.h create mode 100644 include/libdvbv5/mpeg_pes.h create mode 100644 include/libdvbv5/mpeg_ts.h create mode 100644 include/libdvbv5/nit.h create mode 100644 include/libdvbv5/pat.h create mode 100644 include/libdvbv5/pmt.h create mode 100644 include/libdvbv5/sdt.h create mode 100644 include/libdvbv5/vct.h create mode 100644 include/libv4l-plugin.h create mode 100644 include/libv4l1-videodev.h create mode 100644 include/libv4l1.h create mode 100644 include/libv4l2.h create mode 100644 include/libv4l2rds.h create mode 100644 include/libv4lconvert.h create mode 100644 libv4l2/Makefile create mode 100644 libv4l2/Makefile.am create mode 100644 libv4l2/Makefile.dGPU create mode 100644 libv4l2/libv4l2-priv.h create mode 100644 libv4l2/libv4l2.c create mode 100644 libv4l2/libv4l2.export create mode 100644 libv4l2/libv4l2.pc.in create mode 100644 libv4l2/log.c create mode 100644 libv4l2/v4l2-plugin-android.c create mode 100644 libv4l2/v4l2-plugin.c create mode 100644 libv4l2/v4l2convert.c create mode 100644 libv4lconvert/Makefile create mode 100644 libv4lconvert/Makefile.am create mode 100644 libv4lconvert/Makefile.dGPU create mode 100644 libv4lconvert/bayer.c create mode 100644 libv4lconvert/control/libv4lcontrol-priv.h create mode 100644 libv4lconvert/control/libv4lcontrol.c create mode 100644 libv4lconvert/control/libv4lcontrol.h create mode 100644 libv4lconvert/cpia1.c create mode 100644 libv4lconvert/crop.c create mode 100644 libv4lconvert/flip.c create mode 100644 libv4lconvert/helper-funcs.h create mode 100644 libv4lconvert/helper.c create mode 100644 libv4lconvert/hm12.c create mode 100644 libv4lconvert/jidctflt.c create mode 100644 libv4lconvert/jl2005bcd.c create mode 100644 libv4lconvert/jpeg.c create mode 100644 libv4lconvert/jpeg_memsrcdest.c create mode 100644 libv4lconvert/jpeg_memsrcdest.h create mode 100644 libv4lconvert/jpgl.c create mode 100644 libv4lconvert/libv4lconvert-priv.h create mode 100644 libv4lconvert/libv4lconvert.c create mode 100644 libv4lconvert/libv4lconvert.export create mode 100644 libv4lconvert/libv4lconvert.pc.in create mode 100644 libv4lconvert/libv4lsyscall-priv.h create mode 100644 libv4lconvert/mr97310a.c create mode 100644 libv4lconvert/ov511-decomp.c create mode 100644 libv4lconvert/ov518-decomp.c create mode 100644 libv4lconvert/pac207.c create mode 100644 libv4lconvert/processing/autogain.c create mode 100644 libv4lconvert/processing/gamma.c create mode 100644 libv4lconvert/processing/libv4lprocessing-priv.h create mode 100644 libv4lconvert/processing/libv4lprocessing.c create mode 100644 libv4lconvert/processing/libv4lprocessing.h create mode 100644 libv4lconvert/processing/whitebalance.c create mode 100644 libv4lconvert/rgbyuv.c create mode 100644 libv4lconvert/se401.c create mode 100644 libv4lconvert/sn9c10x.c create mode 100644 libv4lconvert/sn9c2028-decomp.c create mode 100644 libv4lconvert/sn9c20x.c create mode 100644 libv4lconvert/spca501.c create mode 100644 libv4lconvert/spca561-decompress.c create mode 100644 libv4lconvert/sq905c.c create mode 100644 libv4lconvert/stv0680.c create mode 100644 libv4lconvert/tinyjpeg-internal.h create mode 100644 libv4lconvert/tinyjpeg.c create mode 100644 libv4lconvert/tinyjpeg.h create mode 100644 push_info.txt create mode 100644 v4l2libs_README.txt diff --git a/commitFile.txt b/commitFile.txt new file mode 100644 index 0000000..a38faf3 --- /dev/null +++ b/commitFile.txt @@ -0,0 +1,110 @@ +Updating prebuilts and/or headers + +22fd03618c218ae14ba59a5aab5f30b0acd2b6ea - libv4lconvert/Makefile +66dd7958319442bd52ba40ede28fbfe31bb4e074 - libv4lconvert/cpia1.c +3271d74d02e2f33c16e3e50aeb1268eb9c440782 - libv4lconvert/rgbyuv.c +db3c69c666e451c5d4ef6d1b5a3117f4b128baa4 - libv4lconvert/libv4lconvert-priv.h +1f1d1e05443c4b824cd697c0ce5efa9ea1277964 - libv4lconvert/ov518-decomp.c +b694b6348e035b3da880824c2c2768145c9b5199 - libv4lconvert/jpeg_memsrcdest.c +22a502c238e48f4b939c81de41feccfc7c366766 - libv4lconvert/Makefile.dGPU +60e29f95ea52df4407d771330897813cdb38340f - libv4lconvert/libv4lsyscall-priv.h +cc3f3e94a21795990610e63887c30528bde7b42e - libv4lconvert/bayer.c +72953a5a3a56b0188d35f49246356b9c8c35756c - libv4lconvert/helper.c +d6c1aba89bbcb6fef46a6f22b7ea01025435c44d - libv4lconvert/Makefile.am +ddd39b2fe0e2a86a6c64031ccc0d36edfd9b0f1a - libv4lconvert/sn9c10x.c +07f8e7c84abfbbe76d49d8bfd1f4eae6ea39a90b - libv4lconvert/jpgl.c +84c9c3812d4b5f237c8cd616d37fc1161a212acc - libv4lconvert/se401.c +fbbffd8182b4fe2e85289b6e784f70cba7ea7b1d - libv4lconvert/sq905c.c +fa751ff0f78845f3b4591396710df3b165bc8d11 - libv4lconvert/mr97310a.c +6ad4947dca51a7e67e056561cdb445d6c354d23c - libv4lconvert/libv4lconvert.c +4eff5c1a5e0b99ce4d6e9aa63645d9628467fdc3 - libv4lconvert/sn9c2028-decomp.c +5430e46abb1ac7039ed0309ca338237533ff29c9 - libv4lconvert/sn9c20x.c +3c49d99b9753208a9c1c2a9c738a1e7ad291ca22 - libv4lconvert/jpeg_memsrcdest.h +fb3344cfa8df97688332ee4fd3b17968437e8ad5 - libv4lconvert/helper-funcs.h +8b7644ac3d5c4161cfb6dcc2a34013f4c379c665 - libv4lconvert/libv4lconvert.export +be9e3bf3d7d1086b6eed0c1bf2f574c2b1737c00 - libv4lconvert/tinyjpeg.c +f08c902ecd48c2739956606b502fc0b8e4007703 - libv4lconvert/crop.c +f4d73412805f12fa08dd79a43798a7f8d7acece9 - libv4lconvert/pac207.c +25130d299463897a09e8b9adf72389dac2e89fa4 - libv4lconvert/tinyjpeg-internal.h +463725aa4dd3fecaf89c0b8bbf4747f8f7577935 - libv4lconvert/jpeg.c +725c9b8d0bfadba566cf200921e602961cb12705 - libv4lconvert/spca561-decompress.c +ff7444c48a8da88f8a466cfb138e30e585828cb3 - libv4lconvert/jl2005bcd.c +803c4d0b9364050eda163452b8792e62e221ab6d - libv4lconvert/tinyjpeg.h +cc8982bb6f753249181c715fe6430ffefc78c23b - libv4lconvert/stv0680.c +3e8e6c1fb85e3c4b58c4e9b2b0a223ddc793edcb - libv4lconvert/libv4lconvert.pc.in +033894511bd7e8a374a52486889658faa17918c4 - libv4lconvert/flip.c +f061a4e0e45ca8e0dbab630dd477e19a6c915fda - libv4lconvert/spca501.c +b2c19c2eac71d39d3fb883cdc159a69c2afa8fd6 - libv4lconvert/ov511-decomp.c +1d9c446cd8a232da87bd79acebc93e018ec72499 - libv4lconvert/jidctflt.c +1949e23fe99ccd0a05dcd084848f6d38b0af7ab6 - libv4lconvert/hm12.c +7da402829dbff238ca6ac829c037a85476185db6 - libv4lconvert/processing/autogain.c +a54c2cb0439e606af01d0b4f02704f411819d98c - libv4lconvert/processing/libv4lprocessing.c +dae9c69b7f019d7d4494cd56e2cf757e8510824a - libv4lconvert/processing/whitebalance.c +ebf12bcf99f35fb9c400b04a1439e68598268249 - libv4lconvert/processing/gamma.c +0390d660eb130f0e580832bcf8ad5069010d2696 - libv4lconvert/processing/libv4lprocessing.h +33ab91b54108e8c24cbb80c5c335d96391d440b2 - libv4lconvert/processing/libv4lprocessing-priv.h +19a7fd04cdeba61172f281806d030472dee79fcd - libv4lconvert/control/libv4lcontrol.c +1e08fb01a598d71e3fc69656c4f2291f7dc13105 - libv4lconvert/control/libv4lcontrol.h +70f4992835e964b2698473971904375333e3659b - libv4lconvert/control/libv4lcontrol-priv.h +6feb5b2b8c99c99712dd1ea7fe9ab674d58bf86b - include/libv4l1.h +bc44111fd6b2f0374a9fc67b1b23666c5c498b2c - include/libv4l2rds.h +f751b481c4a9203345cdbb6459d0f2882f7cdbd9 - include/libv4lconvert.h +1edc439e6c0fc98513fa4a69557eb6221d043be0 - include/libv4l2.h +f2b73fa5ab10ea7038e58bd9a4461d8e16316249 - include/libv4l1-videodev.h +94434b9692371b7d5f54ddef2141d22d90079ce9 - include/libv4l-plugin.h +c84a9a115a21d1fd20da0f6ca3df7b46dd23cd2a - include/config.h +1ba874a7cad36ff31e4af3bfb37b98c05063d6b2 - include/libdvbv5/desc_event_extended.h +6bd2ed0beaf6aa4838e239198564fd8e1d20a3a1 - include/libdvbv5/desc_t2_delivery.h +92d4c28148d0b537c8afc289e1a76de68435cba0 - include/libdvbv5/dvb-scan.h +22c83d133e5c1d2648efb3028e0d89c970d0aad4 - include/libdvbv5/desc_partial_reception.h +98365b48442b9e3abb58101983b5da8c14f78289 - include/libdvbv5/dvb-v5-std.h +2f55ba765c689500401111747bb381b5aca77b30 - include/libdvbv5/desc_ca.h +100c02ce3bc364ddff895c75f4fb1f928a748d2d - include/libdvbv5/desc_cable_delivery.h +96db22ef84892a36d5df3cffa0b30d5bad01939c - include/libdvbv5/desc_logical_channel.h +7645dda247bcd45628afbb74ec2707a47050992e - include/libdvbv5/nit.h +30e9a7240938943de2725f2b335b19ad320179a5 - include/libdvbv5/header.h +c1212a9308d96730de547648d3cda2fc144d0e29 - include/libdvbv5/desc_atsc_service_location.h +5b4a5e7fb30a7f28118be012837e73a7151d2619 - include/libdvbv5/cat.h +2560f18846a535a2c02e1ae449511e731f11c011 - include/libdvbv5/desc_ca_identifier.h +450fab787e61210c0c5f527df92c31c90b44a113 - include/libdvbv5/desc_service.h +cabecc6d7c9fdf1c437273bd6a746bf83c156f72 - include/libdvbv5/desc_frequency_list.h +7a6093b13354d054cac78ea118a96e813cac3395 - include/libdvbv5/atsc_eit.h +9a2b20076d6728b5799096e4149e33a73119e1ef - include/libdvbv5/desc_sat.h +146f4f53fc49c66b59905249c0142efffd72fc54 - include/libdvbv5/desc_network_name.h +efa3a711499f68ae370d49d98dc1963bf6bafcd8 - include/libdvbv5/desc_extension.h +44ab16a8d4eae09690c71a6301927c1da55dda6d - include/libdvbv5/descriptors.h +c18291ff9009bfe71a2c7c6f0fce75331dc95e30 - include/libdvbv5/sdt.h +5e2dfc1d9a71805389e9a7932812695d0309050c - include/libdvbv5/dvb-frontend.h +e81b7f75c11f175cf365fc7fb535e80828f10e24 - include/libdvbv5/dvb-file.h +d7a096d51e3050c8f52e0e2111d88b71a5313da1 - include/libdvbv5/dvb-demux.h +bdf514383ca0afe981cf4fd6af86440db2dc6667 - include/libdvbv5/pat.h +b72b6d1ffcdd81e3e631c7c20bb30e5c287dc7ff - include/libdvbv5/vct.h +9b5cfad4a5f41cbf886507da6e79b07314827b32 - include/libdvbv5/desc_language.h +6e6fd4c61c1f61006c63214cbe4868d49428ddb9 - include/libdvbv5/mpeg_pes.h +188fc2cbec97288787a7f66554a4b6288224f980 - include/libdvbv5/desc_isdbt_delivery.h +73b7b0cf684de0e8a4eae49a8521f81b411d7b72 - include/libdvbv5/desc_ts_info.h +7544b5fb8f621a9c637c40d8f7a2a71f6ab4bd63 - include/libdvbv5/desc_hierarchy.h +9d523ee179af955a687662996050ee3cfaacf2ab - include/libdvbv5/crc32.h +7fb0966c6a1ccdf1a8844aed4a94d4ae1d02fcd7 - include/libdvbv5/dvb-fe.h +c8b4fc511833f0993fa740a529e1f61e0f5a216f - include/libdvbv5/mpeg_es.h +ac87e3306569dae329809f27ef227c5d50f0b60e - include/libdvbv5/desc_event_short.h +40a06b5375dbc0de88a15d26cc6c1e9a505119bc - include/libdvbv5/eit.h +ef979f3276cc3cad6e947865a42643fbba860c69 - include/libdvbv5/mgt.h +b867a2e7941d718aa64b2f6a1402322b616cb2da - include/libdvbv5/pmt.h +fb8d640da36b6a156cbe0ef12dc25468de89a2a1 - include/libdvbv5/dvb-sat.h +4c412880f0c49cd00cb16e56eed082c4744211a5 - include/libdvbv5/countries.h +ad13bfa0b1642fc72cca387e62bc193974c8d5ee - include/libdvbv5/atsc_header.h +02168c58e3c772f116f075085579ac4a8422e819 - include/libdvbv5/desc_terrestrial_delivery.h +4fe7def34ff640fc5e327b3596298169fdfe2f1c - include/libdvbv5/mpeg_ts.h +d562371bb8a3b961c4d63a0f5618453bdff4bcd3 - include/libdvbv5/dvb-log.h +2542aabb7fbff4b1a09faaadec6006c4410a6d10 - libv4l2/libv4l2-priv.h +4ba98a607592ed0b8327b387af354544c65c9b67 - libv4l2/v4l2-plugin-android.c +752cb342c44989a8c172e3280e220a6fa2ec86b5 - libv4l2/Makefile +ffecae84262f548deac1da0fa51f1aba6b6f96a0 - libv4l2/Makefile.dGPU +766aaca553b0166eb736557e44ad42b69464aa53 - libv4l2/libv4l2.export +e6516370c43e4869e05a540d2e4ef584ac64890a - libv4l2/v4l2-plugin.c +8e335567bf404eeb3d180dd384309f687f2ab944 - libv4l2/Makefile.am +d1f2b6f016cfb90c616d848418feb915e3737fa7 - libv4l2/libv4l2.c +7fa618184ff89737d13164be0b79e227d81f398c - libv4l2/log.c +cbcee4426c19c168c6f49d04af3a0b2e30c0b681 - libv4l2/libv4l2.pc.in +9d456d1772885d900865a8958c0291e13d509de5 - libv4l2/v4l2convert.c diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..bf5d3f4 --- /dev/null +++ b/include/config.h @@ -0,0 +1,220 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#define ENABLE_NLS 1 + +/* alsa library is present */ +#define HAVE_ALSA 1 + +/* glibc has functions to provide stack backtrace */ +#define HAVE_BACKTRACE 1 + +/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the + CoreFoundation framework. */ +/* #undef HAVE_CFLOCALECOPYCURRENT */ + +/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in + the CoreFoundation framework. */ +/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */ + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#define HAVE_DCGETTEXT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Usage of DVBv5 remote enabled */ +#define HAVE_DVBV5_REMOTE 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#define HAVE_GETTEXT 1 + +/* Define if you have the iconv() function and it works. */ +#define HAVE_ICONV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* whether we use libjpeg */ +//#define HAVE_JPEG 0 + +/* Define to 1 if you have the `klogctl' function. */ +#define HAVE_KLOGCTL 1 + +/* Use libudev */ +#define HAVE_LIBUDEV /**/ + +/* whether to use libv4lconvert helpers */ +#define HAVE_LIBV4LCONVERT_HELPERS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Have ioctl with POSIX signature */ +/* #undef HAVE_POSIX_IOCTL */ + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* qt has opengl support */ +#define HAVE_QTGL 1 + +/* Define to 1 if you have the `secure_getenv' function. */ +#define HAVE_SECURE_GETENV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_KLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* V4L plugin support enabled */ +#define HAVE_V4L_PLUGINS 1 + +/* Define to 1 or 0, depending whether the compiler supports simple visibility + declarations. */ +#define HAVE_VISIBILITY 1 + +/* Define to 1 if you have the `__secure_getenv' function. */ +/* #undef HAVE___SECURE_GETENV */ + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST + +/* ir-keytable preinstalled tables directory */ +#define IR_KEYTABLE_SYSTEM_DIR "/lib/udev/rc_keymaps" + +/* ir-keytable user defined tables directory */ +#define IR_KEYTABLE_USER_DIR "/usr/local/etc/rc_keymaps" + +/* libdvbv5 domain */ +#define LIBDVBV5_DOMAIN "libdvbv5" + +/* libv4l1 private lib directory */ +#define LIBV4L1_PRIV_DIR "/usr/local/lib/libv4l" + +/* libv4l2 plugin directory */ +#ifdef LIBV4L2_PLUGIN_DIR_PATH_X86 +#define LIBV4L2_PLUGIN_DIR "/usr/lib/x86_64-linux-gnu/libv4l/plugins" +#else +#define LIBV4L2_PLUGIN_DIR "/usr/lib/aarch64-linux-gnu/libv4l/plugins/nv" +#endif +/* libv4l2 private lib directory */ +#define LIBV4L2_PRIV_DIR "/usr/local/lib/libv4l" + +/* libv4lconvert private lib directory */ +#define LIBV4LCONVERT_PRIV_DIR "/usr/local/lib/libv4l" + +/* locale directory */ +#define LOCALEDIR "/usr/local/share/locale" + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if `major', `minor', and `makedev' are declared in . + */ +/* #undef MAJOR_IN_MKDEV */ + +/* Define to 1 if `major', `minor', and `makedev' are declared in + . */ +/* #undef MAJOR_IN_SYSMACROS */ + +/* Name of package */ +#define PACKAGE "v4l-utils" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "v4l-utils" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "v4l-utils 1.14.2" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "v4l-utils" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.14.2" + +/* Define to the type that is the result of default argument promotions of + type mode_t. */ +#define PROMOTED_MODE_T mode_t + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* v4l-utils version string */ +#define V4L_UTILS_VERSION "1.14.2" + +/* Version number of package */ +#define VERSION "1.14.2" + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define to `int' if does not define. */ +/* #undef mode_t */ diff --git a/include/libdvbv5/atsc_eit.h b/include/libdvbv5/atsc_eit.h new file mode 100644 index 0000000..45c6d32 --- /dev/null +++ b/include/libdvbv5/atsc_eit.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file atsc_eit.h + * @ingroup dvb_table + * @brief Provides the table parser for the ATSC EIT (Event Information Table) + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ATSC A/65:2009 + * + * @see + * http://www.etherguidesystems.com/help/sdos/atsc/syntax/tablesections/eitks.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _ATSC_EIT_H +#define _ATSC_EIT_H + +#include +#include /* ssize_t */ +#include + +#include + +/** + * @def ATSC_TABLE_EIT + * @brief ATSC EIT table ID + * @ingroup dvb_table + */ +#define ATSC_TABLE_EIT 0xCB + +/** + * @struct atsc_table_eit_event + * @brief ATSC EIT event table + * @ingroup dvb_table + * + * @param event_id an uniquelly (inside a service ID) event ID + * @param title_length title length. Zero means no title + * @param duration duration in seconds + * @param etm Extended Text Message location + * @param descriptor pointer to struct dvb_desc + * @param next pointer to struct atsc_table_eit_event + * @param start event start (in struct tm format) + * @param source_id source id (obtained from ATSC header) + * + * This structure is used to store the original ATSC EIT event table, + * converting the integer fields to the CPU endianness, and converting the + * timestamps to a way that it is better handled on Linux. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after atsc_table_eit_event::descriptor (including it) won't + * be bit-mapped to the data parsed from the MPEG TS. So, metadata are added + * there. + */ +struct atsc_table_eit_event { + union { + uint16_t bitfield; + struct { + uint16_t event_id:14; + uint16_t one:2; + } __attribute__((packed)); + } __attribute__((packed)); + uint32_t start_time; + union { + uint32_t bitfield2; + struct { + uint32_t title_length:8; + uint32_t duration:20; + uint32_t etm:2; + uint32_t one2:2; + uint32_t :2; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct atsc_table_eit_event *next; + struct tm start; + uint16_t source_id; +} __attribute__((packed)); + +/** + * @union atsc_table_eit_desc_length + * @brief ATSC EIT descriptor length + * @ingroup dvb_table + * + * @param desc_length descriptor length + * + * This structure is used to store the original ATSC EIT event table, + * converting the integer fields to the CPU endianness, and converting the + * timestamps to a way that it is better handled on Linux. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + */ +union atsc_table_eit_desc_length { + uint16_t bitfield; + struct { + uint16_t desc_length:12; + uint16_t reserved:4; + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @struct atsc_table_eit + * @brief ATSC EIT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param protocol_version protocol version + * @param events events + * @param event pointer to struct atsc_table_eit_event + * + * This structure is used to store the original ATSC EIT table, + * converting the integer fields to the CPU endianness. + * + * Everything after atsc_table_eit::event (including it) won't + * be bit-mapped to the data parsed from the MPEG TS. So, metadata are added + * there. + */ +struct atsc_table_eit { + struct dvb_table_header header; + uint8_t protocol_version; + uint8_t events; + struct atsc_table_eit_event *event; +} __attribute__((packed)); + +/** + * @brief Macro used to find event on an ATSC EIT table + * @ingroup dvb_table + * + * @param _event event to seek + * @param _eit pointer to struct atsc_table_eit_event + */ +#define atsc_eit_event_foreach(_event, _eit) \ + for( struct atsc_table_eit_event *_event = _eit->event; _event; _event = _event->next ) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses ATSC EIT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the EIT raw data + * @param buflen length of the buffer + * @param table pointer to struct atsc_table_eit to be allocated and filled + * + * This function allocates an ATSC EIT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t atsc_table_eit_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct atsc_table_eit **table); + +/** + * @brief Frees all data allocated by the ATSC EIT table parser + * @ingroup dvb_table + * + * @param table pointer to struct atsc_table_eit to be freed + */ +void atsc_table_eit_free(struct atsc_table_eit *table); + +/** + * @brief Prints the content of the ATSC EIT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct atsc_table_eit + */ +void atsc_table_eit_print(struct dvb_v5_fe_parms *parms, + struct atsc_table_eit *table); + +/** + * @brief Converts an ATSC EIT formatted timestamp into struct tm + * @ingroup ancillary + * + * @param start_time event on ATSC EIT time format + * @param tm pointer to struct tm where the converted timestamp will + * be stored. + */ +void atsc_time(const uint32_t start_time, struct tm *tm); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/atsc_header.h b/include/libdvbv5/atsc_header.h new file mode 100644 index 0000000..ca9bc84 --- /dev/null +++ b/include/libdvbv5/atsc_header.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _ATSC_HEADER_H +#define _ATSC_HEADER_H + +/** + * @file atsc_header.h + * @ingroup dvb_table + * @brief Provides some common ATSC stuff + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#include +#include /* ssize_t */ + +#include + +/** + * @def ATSC_BASE_PID + * @brief ATSC PID for the Program and System Information Protocol + * @ingroup dvb_table + */ +#define ATSC_BASE_PID 0x1FFB + +#ifndef _DOXYGEN + +/* Deprecated, as it causes troubles with doxygen */ +#define ATSC_HEADER() \ + struct dvb_table_header header; \ + uint8_t protocol_version; \ + +#define ATSC_TABLE_HEADER_PRINT(_parms, _table) \ + dvb_table_header_print(_parms, &_table->header); \ + dvb_loginfo("| protocol_version %d", _table->protocol_version); \ + +#endif /* _DOXYGEN */ + +#endif /* _ATSC_HEADER_H */ diff --git a/include/libdvbv5/cat.h b/include/libdvbv5/cat.h new file mode 100644 index 0000000..ddcd7f5 --- /dev/null +++ b/include/libdvbv5/cat.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file cat.h + * @ingroup dvb_table + * @brief Provides the table parser for the CAT (Conditional Access Table) + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _CAT_H +#define _CAT_H + +#include +#include /* ssize_t */ + +#include + +/** + * @def DVB_TABLE_CAT + * @brief ATSC CAT table ID + * @ingroup dvb_table + * @def DVB_TABLE_CAT_PID + * @brief ATSC PID table ID + * @ingroup dvb_table + */ +#define DVB_TABLE_CAT 0x01 +#define DVB_TABLE_CAT_PID 0x0001 + +/** + * @struct dvb_table_cat + * @brief ATSC CAT table + * + * @param header struct dvb_table_header content + * @param descriptor pointer to struct dvb_desc + */ +struct dvb_table_cat { + struct dvb_table_header header; + struct dvb_desc *descriptor; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses CAT table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the CAT raw data + * @param buflen length of the buffer + * @param table pointer to struct dvb_table_cat to be allocated and filled + * + * This function allocates an CAT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t dvb_table_cat_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct dvb_table_cat **table); + +/** + * @brief Frees all data allocated by the CAT table parser + * + * @param table pointer to struct dvb_table_cat to be freed + */ +void dvb_table_cat_free(struct dvb_table_cat *table); + +/** + * @brief Prints the content of the CAT table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_cat + */ +void dvb_table_cat_print(struct dvb_v5_fe_parms *parms, + struct dvb_table_cat *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/countries.h b/include/libdvbv5/countries.h new file mode 100644 index 0000000..9f4b679 --- /dev/null +++ b/include/libdvbv5/countries.h @@ -0,0 +1,869 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Winfried Koehler + * Copyright (C) 2014 Akihiro Tsukada + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. if not, see . + * + */ + +/** + * @file countries.h + * @ingroup ancillary + * @brief Provides ancillary code to convert ISO 3166-1 country codes + * @copyright GNU General Public License version 2 (GPLv2) + * @author Winfried Koehler + * @author Akihiro Tsukada + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _COUNTRIES_H_ +#define _COUNTRIES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @enum dvb_country_t + * @brief ISO-3166-1 alpha-2 country code + * @ingroup ancillary + * + * @var COUNTRY_UNKNOWN + * @brief (Unknown Country) + * @var AD + * @brief Andorra + * @var AE + * @brief United Arab Emirates + * @var AF + * @brief Afghanistan + * @var AG + * @brief Antigua and Barbuda + * @var AI + * @brief Anguilla + * @var AL + * @brief Albania + * @var AM + * @brief Armenia + * @var AO + * @brief Angola + * @var AQ + * @brief Antarctica + * @var AR + * @brief Argentina + * @var AS + * @brief American Samoa + * @var AT + * @brief Austria + * @var AU + * @brief Australia + * @var AW + * @brief Aruba + * @var AX + * @brief Aland Islands + * @var AZ + * @brief Azerbaijan + * @var BA + * @brief Bosnia and Herzegovina + * @var BB + * @brief Barbados + * @var BD + * @brief Bangladesh + * @var BE + * @brief Belgium + * @var BF + * @brief Burkina Faso + * @var BG + * @brief Bulgaria + * @var BH + * @brief Bahrain + * @var BI + * @brief Burundi + * @var BJ + * @brief Benin + * @var BL + * @brief Saint Barthelemy + * @var BM + * @brief Bermuda + * @var BN + * @brief Brunei Darussalam + * @var BO + * @brief Plurinational State of Bolivia + * @var BQ + * @brief Bonaire, Saint Eustatius and Saba + * @var BR + * @brief Brazil + * @var BS + * @brief Bahamas + * @var BT + * @brief Bhutan + * @var BV + * @brief Bouvet Island + * @var BW + * @brief Botswana + * @var BY + * @brief Belarus + * @var BZ + * @brief Belize + * @var CA + * @brief Canada + * @var CC + * @brief Cocos (Keeling) Islands + * @var CD + * @brief The Democratic Republic of the Congo + * @var CF + * @brief Central African Republic + * @var CG + * @brief Congo + * @var CH + * @brief Switzerland + * @var CI + * @brief Cote d'Ivoire + * @var CK + * @brief Cook Islands + * @var CL + * @brief Chile + * @var CM + * @brief Cameroon + * @var CN + * @brief China + * @var CO + * @brief Colombia + * @var CR + * @brief Costa Rica + * @var CU + * @brief Cuba + * @var CV + * @brief Cape Verde + * @var CW + * @brief Curacao + * @var CX + * @brief Christmas Island + * @var CY + * @brief Cyprus + * @var CZ + * @brief Czech Republic + * @var DE + * @brief Germany + * @var DJ + * @brief Djibouti + * @var DK + * @brief Denmark + * @var DM + * @brief Dominica + * @var DO + * @brief Dominican Republic + * @var DZ + * @brief Algeria + * @var EC + * @brief Ecuador + * @var EE + * @brief Estonia + * @var EG + * @brief Egypt + * @var EH + * @brief Western Sahara + * @var ER + * @brief Eritrea + * @var ES + * @brief Spain + * @var ET + * @brief Ethiopia + * @var FI + * @brief Finland + * @var FJ + * @brief Fiji + * @var FK + * @brief Falkland Islands (Malvinas) + * @var FM + * @brief Federated States of Micronesia + * @var FO + * @brief Faroe Islands + * @var FR + * @brief France + * @var GA + * @brief Gabon + * @var GB + * @brief United Kingdom + * @var GD + * @brief Grenada + * @var GE + * @brief Georgia + * @var GF + * @brief French Guiana + * @var GG + * @brief Guernsey + * @var GH + * @brief Ghana + * @var GI + * @brief Gibraltar + * @var GL + * @brief Greenland + * @var GM + * @brief Gambia + * @var GN + * @brief Guinea + * @var GP + * @brief Guadeloupe + * @var GQ + * @brief Equatorial Guinea + * @var GR + * @brief Greece + * @var GS + * @brief South Georgia and the South Sandwich Islands + * @var GT + * @brief Guatemala + * @var GU + * @brief Guam + * @var GW + * @brief Guinea-Bissau + * @var GY + * @brief Guyana + * @var HK + * @brief Hong Kong + * @var HM + * @brief Heard Island and McDonald Islands + * @var HN + * @brief Honduras + * @var HR + * @brief Croatia + * @var HT + * @brief Haiti + * @var HU + * @brief Hungary + * @var ID + * @brief Indonesia + * @var IE + * @brief Ireland + * @var IL + * @brief Israel + * @var IM + * @brief Isle of Man + * @var IN + * @brief India + * @var IO + * @brief British Indian Ocean Territory + * @var IQ + * @brief Iraq + * @var IR + * @brief Islamic Republic of Iran + * @var IS + * @brief Iceland + * @var IT + * @brief Italy + * @var JE + * @brief Jersey + * @var JM + * @brief Jamaica + * @var JO + * @brief Jordan + * @var JP + * @brief Japan + * @var KE + * @brief Kenya + * @var KG + * @brief Kyrgyzstan + * @var KH + * @brief Cambodia + * @var KI + * @brief Kiribati + * @var KM + * @brief Comoros + * @var KN + * @brief Saint Kitts and Nevis + * @var KP + * @brief Democratic People's Republic of Korea + * @var KR + * @brief Republic of Korea + * @var KW + * @brief Kuwait + * @var KY + * @brief Cayman Islands + * @var KZ + * @brief Kazakhstan + * @var LA + * @brief Lao People's Democratic Republic + * @var LB + * @brief Lebanon + * @var LC + * @brief Saint Lucia + * @var LI + * @brief Liechtenstein + * @var LK + * @brief Sri Lanka + * @var LR + * @brief Liberia + * @var LS + * @brief Lesotho + * @var LT + * @brief Lithuania + * @var LU + * @brief Luxembourg + * @var LV + * @brief Latvia + * @var LY + * @brief Libyan Arab Jamahiriya + * @var MA + * @brief Morocco + * @var MC + * @brief Monaco + * @var MD + * @brief Republic of Moldova + * @var ME + * @brief Montenegro + * @var MF + * @brief Saint Martin (French part) + * @var MG + * @brief Madagascar + * @var MH + * @brief Marshall Islands + * @var MK + * @brief The Former Yugoslav Republic of Macedonia + * @var ML + * @brief Mali + * @var MM + * @brief Myanmar + * @var MN + * @brief Mongolia + * @var MO + * @brief Macao + * @var MP + * @brief Northern Mariana Islands + * @var MQ + * @brief Martinique + * @var MR + * @brief Mauritania + * @var MS + * @brief Montserrat + * @var MT + * @brief Malta + * @var MU + * @brief Mauritius + * @var MV + * @brief Maldives + * @var MW + * @brief Malawi + * @var MX + * @brief Mexico + * @var MY + * @brief Malaysia + * @var MZ + * @brief Mozambique + * @var NA + * @brief Namibia + * @var NC + * @brief New Caledonia + * @var NE + * @brief Niger + * @var NF + * @brief Norfolk Island + * @var NG + * @brief Nigeria + * @var NI + * @brief Nicaragua + * @var NL + * @brief Netherlands + * @var NO + * @brief Norway + * @var NP + * @brief Nepal + * @var NR + * @brief Nauru + * @var NU + * @brief Niue + * @var NZ + * @brief New Zealand + * @var OM + * @brief Oman + * @var PA + * @brief Panama + * @var PE + * @brief Peru + * @var PF + * @brief French Polynesia + * @var PG + * @brief Papua New Guinea + * @var PH + * @brief Philippines + * @var PK + * @brief Pakistan + * @var PL + * @brief Poland + * @var PM + * @brief Saint Pierre and Miquelon + * @var PN + * @brief Pitcairn + * @var PR + * @brief Puerto Rico + * @var PS + * @brief Occupied Palestinian Territory + * @var PT + * @brief Portugal + * @var PW + * @brief Palau + * @var PY + * @brief Paraguay + * @var QA + * @brief Qatar + * @var RE + * @brief Reunion + * @var RO + * @brief Romania + * @var RS + * @brief Serbia + * @var RU + * @brief Russian Federation + * @var RW + * @brief Rwanda + * @var SA + * @brief Saudi Arabia + * @var SB + * @brief Solomon Islands + * @var SC + * @brief Seychelles + * @var SD + * @brief Sudan + * @var SE + * @brief Sweden + * @var SG + * @brief Singapore + * @var SH + * @brief Saint Helena, Ascension and Tristan da Cunha + * @var SI + * @brief Slovenia + * @var SJ + * @brief Svalbard and Jan Mayen + * @var SK + * @brief Slovakia + * @var SL + * @brief Sierra Leone + * @var SM + * @brief San Marino + * @var SN + * @brief Senegal + * @var SO + * @brief Somalia + * @var SR + * @brief Suriname + * @var SS + * @brief South Sudan + * @var ST + * @brief Sao Tome and Principe + * @var SV + * @brief El Salvador + * @var SX + * @brief Sint Maarten (Dutch part) + * @var SY + * @brief Syrian Arab Republic + * @var SZ + * @brief Swaziland + * @var TC + * @brief Turks and Caicos Islands + * @var TD + * @brief Chad + * @var TF + * @brief French Southern Territories + * @var TG + * @brief Togo + * @var TH + * @brief Thailand + * @var TJ + * @brief Tajikistan + * @var TK + * @brief Tokelau + * @var TL + * @brief Timor-Leste + * @var TM + * @brief Turkmenistan + * @var TN + * @brief Tunisia + * @var TO + * @brief Tonga + * @var TR + * @brief Turkey + * @var TT + * @brief Trinidad and Tobago + * @var TV + * @brief Tuvalu + * @var TW + * @brief Taiwan, Province of China + * @var TZ + * @brief United Republic of Tanzania + * @var UA + * @brief Ukraine + * @var UG + * @brief Uganda + * @var UM + * @brief United States Minor Outlying Islands + * @var US + * @brief United States + * @var UY + * @brief Uruguay + * @var UZ + * @brief Uzbekistan + * @var VA + * @brief Holy See (Vatican City State) + * @var VC + * @brief Saint Vincent and The Grenadines + * @var VE + * @brief Bolivarian Republic of Venezuela + * @var VG + * @brief British Virgin Islands + * @var VI + * @brief U.S. Virgin Islands + * @var VN + * @brief Viet Nam + * @var VU + * @brief Vanuatu + * @var WF + * @brief Wallis and Futuna + * @var WS + * @brief Samoa + * @var YE + * @brief Yemen + * @var YT + * @brief Mayotte + * @var ZA + * @brief South Africa + * @var ZM + * @brief Zambia + * @var ZW + * @brief Zimbabwe + */ +enum dvb_country_t { + COUNTRY_UNKNOWN, + + AD, + AE, + AF, + AG, + AI, + AL, + AM, + AO, + AQ, + AR, + AS, + AT, + AU, + AW, + AX, + AZ, + BA, + BB, + BD, + BE, + BF, + BG, + BH, + BI, + BJ, + BL, + BM, + BN, + BO, + BQ, + BR, + BS, + BT, + BV, + BW, + BY, + BZ, + CA, + CC, + CD, + CF, + CG, + CH, + CI, + CK, + CL, + CM, + CN, + CO, + CR, + CU, + CV, + CW, + CX, + CY, + CZ, + DE, + DJ, + DK, + DM, + DO, + DZ, + EC, + EE, + EG, + EH, + ER, + ES, + ET, + FI, + FJ, + FK, + FM, + FO, + FR, + GA, + GB, + GD, + GE, + GF, + GG, + GH, + GI, + GL, + GM, + GN, + GP, + GQ, + GR, + GS, + GT, + GU, + GW, + GY, + HK, + HM, + HN, + HR, + HT, + HU, + ID, + IE, + IL, + IM, + IN, + IO, + IQ, + IR, + IS, + IT, + JE, + JM, + JO, + JP, + KE, + KG, + KH, + KI, + KM, + KN, + KP, + KR, + KW, + KY, + KZ, + LA, + LB, + LC, + LI, + LK, + LR, + LS, + LT, + LU, + LV, + LY, + MA, + MC, + MD, + ME, + MF, + MG, + MH, + MK, + ML, + MM, + MN, + MO, + MP, + MQ, + MR, + MS, + MT, + MU, + MV, + MW, + MX, + MY, + MZ, + NA, + NC, + NE, + NF, + NG, + NI, + NL, + NO, + NP, + NR, + NU, + NZ, + OM, + PA, + PE, + PF, + PG, + PH, + PK, + PL, + PM, + PN, + PR, + PS, + PT, + PW, + PY, + QA, + RE, + RO, + RS, + RU, + RW, + SA, + SB, + SC, + SD, + SE, + SG, + SH, + SI, + SJ, + SK, + SL, + SM, + SN, + SO, + SR, + SS, + ST, + SV, + SX, + SY, + SZ, + TC, + TD, + TF, + TG, + TH, + TJ, + TK, + TL, + TM, + TN, + TO, + TR, + TT, + TV, + TW, + TZ, + UA, + UG, + UM, + US, + UY, + UZ, + VA, + VC, + VE, + VG, + VI, + VN, + VU, + WF, + WS, + YE, + YT, + ZA, + ZM, + ZW, +}; + +/** + * @brief Converts an Unix-like 2-letter Country code into enum dvb_country_t + * @ingroup ancillary + * + * @param name two-letter Country code. + * + * @return It returns the corresponding enum dvb_country_t ID. If not found, + * returns COUNTRY_UNKNOWN. + */ +enum dvb_country_t dvb_country_a2_to_id(const char *name); + +/** + * @brief Converts a 3-letter Country code as used by MPEG-TS tables into + * enum dvb_country_t + * @ingroup ancillary + * + * @param name three-letter Country code. + * + * @return It returns the corresponding enum dvb_country_t ID. If not found, + * returns COUNTRY_UNKNOWN. + */ +enum dvb_country_t dvb_country_a3_to_id(const char *name); + +/** + * @brief Converts an enum dvb_country_t into Unix-like 2-letter Country code + * @ingroup ancillary + * + * @param id enum dvb_country_t ID. + * + * @return It returns the 2-letter country code string that corresponts to the + * Country. If not found, returns NULL. + */ +const char *dvb_country_to_2letters(int id); + +/** + * @brief Converts an enum dvb_country_t into a 3-letter Country code + * as used by MPEG-TS tables + * @ingroup ancillary + * + * @param id enum dvb_country_t ID. + * + * @return It returns the 3-letter country code string that corresponts to the + * Country. If not found, returns NULL. + */ +const char *dvb_country_to_3letters(int id); + +/** + * @brief Converts an enum dvb_country_t into a Country name + * as used by MPEG-TS tables + * @ingroup ancillary + * + * @param id enum dvb_country_t ID. + * + * @return It returns a string with the Country name that corresponts to the + * country. If not found, returns NULL. + */ +const char *dvb_country_to_name(int id); + +/** + * @brief Guess the country code from the Unix environment variables + * @ingroup ancillary + * + * @return It returns the corresponding enum dvb_country_t ID. If not found, + * returns COUNTRY_UNKNOWN. + */ +enum dvb_country_t dvb_guess_user_country(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/crc32.h b/include/libdvbv5/crc32.h new file mode 100644 index 0000000..d184b35 --- /dev/null +++ b/include/libdvbv5/crc32.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012-2014 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file crc32.h + * @ingroup ancillary + * @brief Provides ancillary code to calculate DVB crc32 checksum + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _CRC32_H +#define _CRC32_H + +#include +#include /* size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Calculates the crc-32 as defined at the MPEG-TS specs + * @ingroup ancillary + * + * @param data Pointer to the buffer to be checked + * @param datalen Length of the buffer + * @param crc Initial value for the crc checksum. To calculate the + * checksum of the entire packet at once, use 0xFFFFFFFF + */ +uint32_t dvb_crc32(uint8_t *data, size_t datalen, uint32_t crc); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/libdvbv5/desc_atsc_service_location.h b/include/libdvbv5/desc_atsc_service_location.h new file mode 100644 index 0000000..70298b5 --- /dev/null +++ b/include/libdvbv5/desc_atsc_service_location.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _ATSC_SERVICE_LOCATION_H +#define _ATSC_SERVICE_LOCATION_H + +#include + +/** + * @file desc_atsc_service_location.h + * @ingroup descriptors + * @brief Provides the descriptors for ATSC service location + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ATSC A/53 + * + * @see http://www.etherguidesystems.com/help/sdos/atsc/semantics/descriptors/ServiceLocation.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/** + * @struct atsc_desc_service_location_elementary + * @ingroup descriptors + * @brief service location elementary descriptors + * + * @param stream_type stream type + * @param elementary_pid elementary pid + * @param ISO_639_language_code ISO 639 language code + */ +struct atsc_desc_service_location_elementary { + uint8_t stream_type; + union { + uint16_t bitfield; + struct { + uint16_t elementary_pid:13; + uint16_t reserved:3; + } __attribute__((packed)); + } __attribute__((packed)); + char ISO_639_language_code[3]; +} __attribute__((packed)); + +/** + * @struct atsc_desc_service_location + * @ingroup descriptors + * @brief Describes the elementary streams inside a PAT table for ATSC + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param elementary pointer to struct atsc_desc_service_location_elementary + * @param pcr_pid PCR pid + * @param number_elements number elements + */ +struct atsc_desc_service_location { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + struct atsc_desc_service_location_elementary *elementary; + + union { + uint16_t bitfield; + struct { + uint16_t pcr_pid:13; + uint16_t reserved:3; + } __attribute__((packed)); + } __attribute__((packed)); + + uint8_t number_elements; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the service location descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int atsc_desc_service_location_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, + struct dvb_desc *desc); + +/** + * @brief Prints the content of the service location descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void atsc_desc_service_location_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the service location descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void atsc_desc_service_location_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_ca.h b/include/libdvbv5/desc_ca.h new file mode 100644 index 0000000..653da0c --- /dev/null +++ b/include/libdvbv5/desc_ca.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described at ETSI EN 300 468 V1.11.1 (2010-04) + */ + +/** + * @file desc_ca.h + * @ingroup descriptors + * @brief Provides the descriptors for Conditional Access + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 (2010-04) + * + * @see http://www.etherguidesystems.com/help/sdos/mpeg/semantics/mpeg-2/descriptors/CA_descriptor.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _CA_H +#define _CA_H + +#include + +/** + * @struct dvb_desc_ca + * @ingroup descriptors + * @brief Contains the private data for Conditional Access + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param ca_id Conditional Access ID + * @param ca_pid Conditional Access ID + * @param privdata pointer to private data buffer + * @param privdata_len length of the private data + */ +struct dvb_desc_ca { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint16_t ca_id; + union { + uint16_t bitfield1; + struct { + uint16_t ca_pid:13; + uint16_t reserved:3; + } __attribute__((packed)); + } __attribute__((packed)); + + uint8_t *privdata; + uint8_t privdata_len; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +/** @brief initial descriptor field at dvb_desc_ca struct */ +#define dvb_desc_ca_field_first ca_id +/** @brief last descriptor field at dvb_desc_ca struct */ +#define dvb_desc_ca_field_last privdata + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the CA descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_ca_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + struct dvb_desc *desc); + +/** + * @brief Prints the content of the CA descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_ca_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the CA descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_ca_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_ca_identifier.h b/include/libdvbv5/desc_ca_identifier.h new file mode 100644 index 0000000..75c6b71 --- /dev/null +++ b/include/libdvbv5/desc_ca_identifier.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described at ETSI EN 300 468 V1.11.1 (2010-04) + */ + +/** + * @file desc_ca_identifier.h + * @ingroup descriptors + * @brief Provides the descriptors for the Conditional Access identifier + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 (2010-04) + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _CA_IDENTIFIER_H +#define _CA_IDENTIFIER_H + +#include + +/** + * @struct dvb_desc_ca_identifier + * @ingroup descriptors + * @brief Indicates if a particular bouquet, service or event is associated + * with a CA system + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param caid_count Number of CA IDs + * @param caids CA Identifier IDs + */ +struct dvb_desc_ca_identifier { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint8_t caid_count; + uint16_t *caids; + +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +/** @brief initial descriptor field at dvb_desc_ca_identifier struct */ +#define dvb_desc_ca_identifier_field_first ca_id +/** @brief last descriptor field at dvb_desc_ca_identifier struct */ +#define dvb_desc_ca_identifier_field_last privdata + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the CA identifier descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_ca_identifier_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the CA identifier descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_ca_identifier_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the CA identifier descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_ca_identifier_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_cable_delivery.h b/include/libdvbv5/desc_cable_delivery.h new file mode 100644 index 0000000..95c8a11 --- /dev/null +++ b/include/libdvbv5/desc_cable_delivery.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described at ETSI EN 300 468 V1.11.1 (2010-04) + */ + +/** + * @file desc_cable_delivery.h + * @ingroup descriptors + * @brief Provides the descriptors for the cable delivery system descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 (2010-04) + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _CABLE_DELIVERY_H +#define _CABLE_DELIVERY_H + +#include + +/** + * @struct dvb_desc_cable_delivery + * @ingroup descriptors + * @brief Structure containing the cable delivery system descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param frequency frequency, converted to Hz. + * @param fec_outer FEC outer (typically, Viterbi) + * @param modulation modulation + * @param fec_inner FEC inner (convolutional code) + * @param symbol_rate symbol rate, converted to symbols/sec (bauds) + */ +struct dvb_desc_cable_delivery { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint32_t frequency; + union { + uint16_t bitfield1; + struct { + uint16_t fec_outer:4; + uint16_t reserved_future_use:12; + } __attribute__((packed)); + } __attribute__((packed)); + uint8_t modulation; + union { + uint32_t bitfield2; + struct { + uint32_t fec_inner:4; + uint32_t symbol_rate:28; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the service location descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function initializes and makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * Currently, no memory is allocated internally. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_cable_delivery_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the service location descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_cable_delivery_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief converts from the descriptor's modulation into enum fe_modulation, + * as defined by DVBv5 API. + */ +extern const unsigned dvbc_modulation_table[]; + +/** + * @brief converts from the descriptor's FEC into enum fe_code_rate, + * as defined by DVBv5 API. + */ +extern const unsigned dvbc_fec_table[]; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_event_extended.h b/include/libdvbv5/desc_event_extended.h new file mode 100644 index 0000000..9b6262f --- /dev/null +++ b/include/libdvbv5/desc_event_extended.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_event_extended.h + * @ingroup descriptors + * @brief Provides the descriptors for the extended event descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _DESC_EVENT_EXTENDED_H +#define _DESC_EVENT_EXTENDED_H + +#include + + +/** + * @struct dvb_desc_event_extended + * @ingroup descriptors + * @brief Structure containing the extended event descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param id descriptor number + * @param last_id last descriptor number + * @param language ISO 639 language code + * @param text text string + * @param text_emph text emphasis string + * + * @details + * The emphasis text is the one that uses asterisks. For example, in the text: + * "the quick *fox* jumps over the lazy table" the emphasis would be "fox". + */ +struct dvb_desc_event_extended { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + union { + struct { + uint8_t last_id:4; + uint8_t id:4; + } __attribute__((packed)); + uint8_t ids; + } __attribute__((packed)); + + unsigned char language[4]; + char *text; + char *text_emph; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the extended event descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_event_extended_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the extended event descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_event_extended_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the extended event descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_event_extended_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_event_short.h b/include/libdvbv5/desc_event_short.h new file mode 100644 index 0000000..6c80505 --- /dev/null +++ b/include/libdvbv5/desc_event_short.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_event_short.h + * @ingroup descriptors + * @brief Provides the descriptors for the short event descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _DESC_EVENT_SHORT_H +#define _DESC_EVENT_SHORT_H + +#include + +/** + * @struct dvb_desc_event_short + * @ingroup descriptors + * @brief Structure containing the short event descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param language ISO 639 language code + * @param name event name string + * @param name_emph event name emphasis string + * @param text event text string + * @param text_emph event text emphasis string + * + * @details + * The emphasis text is the one that uses asterisks. For example, in the text: + * "the quick *fox* jumps over the lazy table" the emphasis would be "fox". + */ +struct dvb_desc_event_short { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + unsigned char language[4]; + char *name; + char *name_emph; + char *text; + char *text_emph; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the short event descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_event_short_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the short event descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_event_short_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the short event descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_event_short_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_extension.h b/include/libdvbv5/desc_extension.h new file mode 100644 index 0000000..88e931e --- /dev/null +++ b/include/libdvbv5/desc_extension.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2013-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_extension.h + * @ingroup descriptors + * @brief Provides the descriptors for the extension descriptor. + * The extension descriptor is used to extend the 8-bit namespace + * of the descriptor_tag field. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Relevant specs + * The descriptor described herein is defined at: + * + * @see + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _EXTENSION_DESC_H +#define _EXTENSION_DESC_H + +#include + +struct dvb_v5_fe_parms; + +/** + * @enum extension_descriptors + * @brief List containing all extended descriptors used by Digital TV MPEG-TS + * as defined at ETSI EN 300 468 V1.11.1 + * @ingroup descriptors + * + * @var image_icon_descriptor + * @brief image icon descriptor + * + * @var cpcm_delivery_signalling_descriptor + * @brief Content Protection/Copy Management (CPCM) delivery signalling + * descriptor + * + * @var CP_descriptor + * @brief Content Protection descriptor + * + * @var CP_identifier_descriptor + * @brief Content Protection identifier descriptor + * + * @var T2_delivery_system_descriptor + * @brief DVB-T2 delivery system descriptor + * + * @var SH_delivery_system_descriptor + * @brief DVB-SH delivery system descriptor + * + * @var supplementary_audio_descriptor + * @brief supplementary audio descriptor + * + * @var network_change_notify_descriptor + * @brief network change notify descriptor + * + * @var message_descriptor + * @brief message descriptor + * + * @var target_region_descriptor + * @brief target region descriptor + * + * @var target_region_name_descriptor + * @brief target region name descriptor + * + * @var service_relocated_descriptor + * @brief service relocated descriptor + */ +enum extension_descriptors { + image_icon_descriptor = 0x00, + cpcm_delivery_signalling_descriptor = 0x01, + CP_descriptor = 0x02, + CP_identifier_descriptor = 0x03, + T2_delivery_system_descriptor = 0x04, + SH_delivery_system_descriptor = 0x05, + supplementary_audio_descriptor = 0x06, + network_change_notify_descriptor = 0x07, + message_descriptor = 0x08, + target_region_descriptor = 0x09, + target_region_name_descriptor = 0x0a, + service_relocated_descriptor = 0x0b, +}; + +/** + * @struct dvb_extension_descriptor + * @ingroup descriptors + * @brief Structure containing the extended descriptors + * + * @param type Descriptor type + * @param length Length of the descriptor + * @param next pointer to the dvb_desc descriptor + * @param extension_code extension code + * @param descriptor pointer to struct dvb_desc + */ +struct dvb_extension_descriptor { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint8_t extension_code; + + struct dvb_desc *descriptor; +} __attribute__((packed)); + + +/** + * @brief Function prototype for the extended descriptors parsing init code + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param buf buffer with the content of the descriptor + * @param ext struct dvb_extension_descriptor pointer + * @param desc struct dvb_desc pointer + */ +typedef int (*dvb_desc_ext_init_func) (struct dvb_v5_fe_parms *parms, + const uint8_t *buf, + struct dvb_extension_descriptor *ext, + void *desc); +/** + * @brief Function prototype for the extended descriptors parsing print code + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param buf buffer with the content of the descriptor + * @param ext struct dvb_extension_descriptor pointer + * @param desc struct dvb_desc pointer + */ +typedef void (*dvb_desc_ext_print_func)(struct dvb_v5_fe_parms *parms, + const struct dvb_extension_descriptor *ext, + const void *desc); +/** + * @brief Function prototype for the extended descriptors parsing free code + * @ingroup dvb_table + * + * @param desc struct dvb_desc pointer + */ +typedef void (*dvb_desc_ext_free_func) (const void *desc); + +/** + * @struct dvb_ext_descriptor + * @ingroup descriptors + * @brief Structure that describes the parser functions for the extended + * descriptors. Works on a similar way as struct dvb_descriptor. + * + * @param name name of the descriptor + * @param init init dvb_desc_ext_init_func pointer + * @param print print dvb_desc_ext_print_func pointer + * @param free free dvb_desc_ext_free_func pointer + * @param size size of the descriptor + */ +struct dvb_ext_descriptor { + const char *name; + dvb_desc_ext_init_func init; + dvb_desc_ext_print_func print; + dvb_desc_ext_free_func free; + ssize_t size; +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the extended descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_extension_descriptor_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the extended descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_extension_descriptor_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the extended descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_extension_descriptor_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_frequency_list.h b/include/libdvbv5/desc_frequency_list.h new file mode 100644 index 0000000..330e757 --- /dev/null +++ b/include/libdvbv5/desc_frequency_list.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _DESC_FREQUENCY_LIST_H +#define _DESC_FREQUENCY_LIST_H + +#include +/** + * @file desc_frequency_list.h + * @ingroup descriptors + * @brief Provides the descriptors for the frequency list descriptor. + * This descriptor lists the additional frequencies used in transmission + * of a multiplex on other frequencies. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/** + * @struct dvb_desc_frequency_list + * @ingroup descriptors + * @brief Struct containing the frequency list descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param frequencies number of frequencies in the frequency vector + * @param frequency vector with the centre frequency + * @param freq_type freq type, being: 0 = undefined, + * 1 = satelite, 2 = cable or 3 = terrestrial. + */ +struct dvb_desc_frequency_list { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint8_t frequencies; + uint32_t *frequency; + + union { + uint8_t bitfield; + struct { + uint8_t freq_type:2; + uint8_t reserved:6; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the frequency list descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function initializes and makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * Currently, no memory is allocated internally. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_frequency_list_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the frequency list descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_frequency_list_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_hierarchy.h b/include/libdvbv5/desc_hierarchy.h new file mode 100644 index 0000000..1dfd84e --- /dev/null +++ b/include/libdvbv5/desc_hierarchy.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_hierarchy.h + * @ingroup descriptors + * @brief Provides the descriptors for the hierarchy descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ISO/IEC 13818-1 + * + * @see + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _HIERARCHY_H +#define _HIERARCHY_H + +#include + +/** + * @struct dvb_desc_hierarchy + * @ingroup descriptors + * @brief Structure containing the hierarchy descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param hierarchy_type hierarchy type + * @param layer hierarchy layer index + * @param embedded_layer hierarchy embedded layer index + * @param channel hierarchy channel + */ +struct dvb_desc_hierarchy { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint8_t hierarchy_type:4; + uint8_t reserved:4; + + uint8_t layer:6; + uint8_t reserved2:2; + + uint8_t embedded_layer:6; + uint8_t reserved3:2; + + uint8_t channel:6; + uint8_t reserved4:2; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the hierarchy descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function initializes and makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * Currently, no memory is allocated internally. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_hierarchy_init (struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the hierarchy descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_hierarchy_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_isdbt_delivery.h b/include/libdvbv5/desc_isdbt_delivery.h new file mode 100644 index 0000000..05e6d6e --- /dev/null +++ b/include/libdvbv5/desc_isdbt_delivery.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2013-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described on ARIB STD-B10 as Terrestrial delivery system descriptor + */ + +/** + * @file desc_isdbt_delivery.h + * @ingroup descriptors + * @brief Provides the descriptors for the ISDB-T terrestrial delivery system + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ARIB STD-B10 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _ISDBT_DELIVERY_H +#define _ISDBT_DELIVERY_H + +#include + +/** + * @struct isdbt_desc_terrestrial_delivery_system + * @ingroup descriptors + * @brief Struct containing the ISDB-T terrestrial delivery system + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param area_code area code. The area code definition varies from + * Country to Country. + * @param guard_interval guard interval + * @param transmission_mode transmission mode + * @param frequency vector with center frequencies + * @param num_freqs number of frequencies at the + * isdbt_desc_terrestrial_delivery_system::frequency vector + */ +struct isdbt_desc_terrestrial_delivery_system { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint32_t *frequency; + unsigned num_freqs; + + union { + uint16_t bitfield; + struct { + uint16_t transmission_mode:2; + uint16_t guard_interval:2; + uint16_t area_code:12; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the ISDB-T terrestrial delivery system + * descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int isdbt_desc_delivery_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the ISDB-T terrestrial delivery system + * descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void isdbt_desc_delivery_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the ISDB-T terrestrial delivery system + * descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void isdbt_desc_delivery_free(struct dvb_desc *desc); + +/** + * Converts an ISDB-T Interval code into a string + */ +extern const uint32_t isdbt_interval[]; + +/** + * Converts an ISDB-T mode into a string + */ +extern const uint32_t isdbt_mode[]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_language.h b/include/libdvbv5/desc_language.h new file mode 100644 index 0000000..f333cfa --- /dev/null +++ b/include/libdvbv5/desc_language.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_language.h + * @ingroup descriptors + * @brief Provides the descriptors for the ISO639 language descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ISO/IEC 13818-1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _DESC_LANGUAGE_H +#define _DESC_LANGUAGE_H + +#include + +/** + * @struct dvb_desc_language + * @ingroup descriptors + * @brief Structure containing the ISO639 language descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param language ISO639 language string + * @param audio_type audio type: 0 = undefined, 1 = clean effects, + * 2 = hearing impaired, 3 = visual impired + * comentary, other values are reserved. + */ +struct dvb_desc_language { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + unsigned char language[4]; + uint8_t audio_type; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the language descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function initializes and makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * Currently, no memory is allocated internally. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_language_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + struct dvb_desc *desc); + +/** + * @brief Prints the content of the language descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_language_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_logical_channel.h b/include/libdvbv5/desc_logical_channel.h new file mode 100644 index 0000000..69d263d --- /dev/null +++ b/include/libdvbv5/desc_logical_channel.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described on IEC/CENELEC DS/EN 62216-1:2011 + * + * I couldn't find the original version, so I used what's there at: + * http://tdt.telecom.pt/recursos/apresentacoes/Signalling Specifications for DTT deployment in Portugal.pdf + */ + +/** + * @file desc_logical_channel.h + * @ingroup descriptors + * @brief Provides the descriptors for the LCN - Logican Channel Number + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - IEC/CENELEC DS/EN 62216-1:2011 + * + * @see http://tdt.telecom.pt/recursos/apresentacoes/Signalling Specifications for DTT deployment in Portugal.pdf + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _LCN_DESC_H +#define _LCN_DESC_H + +#include + +/** + * @struct dvb_desc_logical_channel_number + * @ingroup descriptors + * @brief Structure containing the logical channel number entires + * + * @param service_id service id + * @param visible_service_flag visible service flag + * @param logical_channel_number logical channel number + */ + +struct dvb_desc_logical_channel_number { + uint16_t service_id; + union { + uint16_t bitfield; + struct { + uint16_t logical_channel_number:10; + uint16_t reserved:5; + uint16_t visible_service_flag:1; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @struct dvb_desc_logical_channel + * @ingroup descriptors + * @brief Structure containing the logical channel number descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param lcn pointer to struct dvb_desc_logical_channel_number + */ +struct dvb_desc_logical_channel { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + struct dvb_desc_logical_channel_number *lcn; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the logical channel number descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_logical_channel_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the logical channel number descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_logical_channel_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the logical channel number descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_logical_channel_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_network_name.h b/include/libdvbv5/desc_network_name.h new file mode 100644 index 0000000..f4af74f --- /dev/null +++ b/include/libdvbv5/desc_network_name.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_network_name.h + * @ingroup descriptors + * @brief Provides the descriptors for the network name descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 (2010-04) + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _DESC_NETWORK_NAME_H +#define _DESC_NETWORK_NAME_H + +#include + +/** + * @struct dvb_desc_network_name + * @ingroup descriptors + * @brief Struct containing the network name descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param network_name network name string + * @param network_name_emph network name emphasis string + * + * @details + * The emphasis text is the one that uses asterisks. For example, in the text: + * "the quick *fox* jumps over the lazy table" the emphasis would be "fox". + */ +struct dvb_desc_network_name { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + char *network_name; + char *network_name_emph; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the network name descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_network_name_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the network name descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_network_name_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the network name descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_network_name_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_partial_reception.h b/include/libdvbv5/desc_partial_reception.h new file mode 100644 index 0000000..b9c8c6a --- /dev/null +++ b/include/libdvbv5/desc_partial_reception.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described on IEC/CENELEC DS/EN 62216-1:2011 + * + * I couldn't find the original version, so I used what's there at: + * http://tdt.telecom.pt/recursos/apresentacoes/Signalling Specifications for DTT deployment in Portugal.pdf + */ + +/** + * @file desc_partial_reception.h + * @ingroup descriptors + * @brief Provides the descriptors for the ISDB partial reception descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - IEC/CENELEC DS/EN 62216-1:2011 + * + * @see http://tdt.telecom.pt/recursos/apresentacoes/Signalling Specifications for DTT deployment in Portugal.pdf + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + + +#ifndef _PARTIAL_RECEPTION_H +#define _PARTIAL_RECEPTION_H + +#include + +/** + * @struct isdb_partial_reception_service_id + * @ingroup descriptors + * @brief Service ID that uses partial reception + * + * @param service_id service id + */ +struct isdb_partial_reception_service_id { + uint16_t service_id; +} __attribute__((packed)); + +/** + * @struct isdb_desc_partial_reception + * @ingroup descriptors + * @brief Structure containing the partial reception descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param partial_reception vector of struct isdb_partial_reception_service_id. + * The length of the vector is given by: + * length / sizeof(struct isdb_partial_reception_service_id). + */ +struct isdb_desc_partial_reception { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + struct isdb_partial_reception_service_id *partial_reception; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the ISDB-T partial reception descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int isdb_desc_partial_reception_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the ISDB-T partial reception descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void isdb_desc_partial_reception_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the ISDB-T partial reception descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void isdb_desc_partial_reception_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_sat.h b/include/libdvbv5/desc_sat.h new file mode 100644 index 0000000..7372442 --- /dev/null +++ b/include/libdvbv5/desc_sat.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_sat.h + * @ingroup descriptors + * @brief Provides the descriptors for the satellite delivery system descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _SAT_H +#define _SAT_H + +#include + +/** + * @struct dvb_desc_sat + * @ingroup descriptors + * @brief Structure containing the satellite delivery system descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param frequency frequency in kHz + * @param orbit orbital position in degrees (multiplied by 10) + * @param west_east west east flag. 0 = west, 1 = east + * @param polarization polarization. 0 = horizontal, 1 = vertical, + * 2 = left, 3 = right. + * @param roll_off roll off alpha factor. 0 = 0.35, 1 = 0.25, + * 2 = 0.20, 3 = reserved. + * @param modulation_system modulation system. 0 = DVB-S, 1 = DVB-S2. + * @param modulation_type modulation type. 0 = auto, 1 = QPSK, 2 = 8PSK, + * 3 = 16-QAM (only for DVB-S2). + * @param symbol_rate symbol rate in Kbauds. + * @param fec inner FEC (convolutional code) + */ +struct dvb_desc_sat { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint32_t frequency; + uint16_t orbit; + uint8_t modulation_type:2; + uint8_t modulation_system:1; + uint8_t roll_off:2; + uint8_t polarization:2; + uint8_t west_east:1; + union { + uint32_t bitfield; + struct { + uint32_t fec:4; + uint32_t symbol_rate:28; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the satellite delivery system descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function initializes and makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * Currently, no memory is allocated internally. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_sat_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the satellite delivery system descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_sat_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief converts from the descriptor's FEC into enum fe_code_rate, + * as defined by DVBv5 API. + */ +extern const unsigned dvbs_dvbc_dvbs_freq_inner[]; + +/** + * @brief converts from the descriptor's polarization into + * enum dvb_sat_polarization, as defined at dvb-v5-std.h. + */ +extern const unsigned dvbs_polarization[]; + +/** + * @brief converts from the descriptor's rolloff into enum fe_rolloff, + * as defined by DVBv5 API. + */ +extern const unsigned dvbs_rolloff[]; + +/** + * @brief converts from the descriptor's modulation into enum fe_modulation, + * as defined by DVBv5 API. + */ +extern const unsigned dvbs_modulation[]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_service.h b/include/libdvbv5/desc_service.h new file mode 100644 index 0000000..5e0cdfb --- /dev/null +++ b/include/libdvbv5/desc_service.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file desc_service.h + * @ingroup descriptors + * @brief Provides the descriptors for the service descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _DESC_SERVICE_H +#define _DESC_SERVICE_H + +#include + +/** + * @struct dvb_desc_service + * @ingroup descriptors + * @brief Structure containing the service descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param service_type service type + * @param name name string + * @param name_emph name emphasis string + * @param provider provider string + * @param provider_emph provider emphasis string + * + * @details + * The emphasis text is the one that uses asterisks. For example, in the text: + * "the quick *fox* jumps over the lazy table" the emphasis would be "fox". + */ +struct dvb_desc_service { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint8_t service_type; + char *name; + char *name_emph; + char *provider; + char *provider_emph; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the service descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_service_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the service descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_service_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the service descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_service_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_t2_delivery.h b/include/libdvbv5/desc_t2_delivery.h new file mode 100644 index 0000000..86cdf03 --- /dev/null +++ b/include/libdvbv5/desc_t2_delivery.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2013-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Based on ETSI EN 300 468 V1.11.1 (2010-04) + */ + +/** + * @file desc_t2_delivery.h + * @ingroup descriptors + * @brief Provides the descriptors for the DVB-T2 delivery system descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _T2_DELIVERY_H +#define _T2_DELIVERY_H + +#include + +/** + * @struct dvb_desc_t2_delivery_subcell + * @ingroup descriptors + * @brief Structure to describe transponder subcell extension and frequencies + * + * @param cell_id_extension cell id extension + * @param transposer_frequency transposer frequency + */ +struct dvb_desc_t2_delivery_subcell { + uint8_t cell_id_extension; + uint16_t transposer_frequency; +} __attribute__((packed)); + +/** + * @struct dvb_desc_t2_delivery + * @ingroup descriptors + * @brief Structure containing the T2 delivery system descriptor + * + * @param plp_id data PLP id + * @param system_id T2 system id + * @param SISO_MISO SISO MISO + * @param bandwidth bandwidth + * @param guard_interval guard interval + * @param transmission_mode transmission mode + * @param other_frequency_flag other frequency flag + * @param tfs_flag tfs flag + * + * @param centre_frequency centre frequency vector + * @param frequency_loop_length size of the dvb_desc_t2_delivery::centre_frequency + * vector + * + * @param subcel_info_loop_length size of the dvb_desc_t2_delivery::subcell + * vector + * @param subcell pointer to struct dvb_desc_t2_delivery_subcell + */ +struct dvb_desc_t2_delivery { + /* extended descriptor */ + + uint8_t plp_id; + uint16_t system_id; + union { + uint16_t bitfield; + struct { + uint16_t tfs_flag:1; + uint16_t other_frequency_flag:1; + uint16_t transmission_mode:3; + uint16_t guard_interval:3; + uint16_t reserved:2; + uint16_t bandwidth:3; + uint16_t SISO_MISO:2; + } __attribute__((packed)); + } __attribute__((packed)); + + uint32_t *centre_frequency; + uint8_t frequency_loop_length; + uint8_t subcel_info_loop_length; + struct dvb_desc_t2_delivery_subcell *subcell; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the T2 delivery system descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param ext struct dvb_extension_descriptor pointer + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_t2_delivery_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, + struct dvb_extension_descriptor *ext, + void *desc); + +/** + * @brief Prints the content of the T2 delivery system descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param ext struct dvb_extension_descriptor pointer + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_t2_delivery_print(struct dvb_v5_fe_parms *parms, + const struct dvb_extension_descriptor *ext, + const void *desc); + +/** + * @brief Frees all data allocated by the T2 delivery system descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_t2_delivery_free(const void *desc); + +/** + * @brief converts from internal representation into bandwidth in Hz + */ +extern const unsigned dvbt2_bw[]; + +/** + * @brief converts from internal representation into enum fe_guard_interval, + * as defined at DVBv5 API. + */ +extern const uint32_t dvbt2_interval[]; + +/** + * @brief converts from the descriptor's transmission mode into + * enum fe_transmit_mode, as defined by DVBv5 API. + */ +extern const unsigned dvbt2_transmission_mode[]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_terrestrial_delivery.h b/include/libdvbv5/desc_terrestrial_delivery.h new file mode 100644 index 0000000..e7212be --- /dev/null +++ b/include/libdvbv5/desc_terrestrial_delivery.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Based on ETSI EN 300 468 V1.11.1 (2010-04) + * + */ + +/** + * @file desc_terrestrial_delivery.h + * @ingroup descriptors + * @brief Provides the descriptors for the DVB-T terrestrial delivery system descriptor + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ETSI EN 300 468 V1.11.1 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _TERRESTRIAL_DELIVERY_H +#define _TERRESTRIAL_DELIVERY_H + +#include + +/** + * @struct dvb_desc_terrestrial_delivery + * @ingroup descriptors + * @brief Structure containing the DVB-T terrestrial delivery system descriptor + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param centre_frequency centre frequency, multiplied by 10 Hz + * @param bandwidth bandwidth + * @param priority priority (0 = LP, 1 = HP) + * @param time_slice_indicator time slicing indicator + * @param mpe_fec_indicator mpe fec indicator. If 1, MPE-FEC is not used. + * @param constellation constellation + * @param hierarchy_information hierarchy information + * @param code_rate_hp_stream code rate hp stream + * @param code_rate_lp_stream code rate lp stream + * @param guard_interval guard interval + * @param transmission_mode transmission mode + * @param other_frequency_flag other frequency flag + */ +struct dvb_desc_terrestrial_delivery { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint32_t centre_frequency; + uint8_t reserved_future_use1:2; + uint8_t mpe_fec_indicator:1; + uint8_t time_slice_indicator:1; + uint8_t priority:1; + uint8_t bandwidth:3; + uint8_t code_rate_hp_stream:3; + uint8_t hierarchy_information:3; + uint8_t constellation:2; + uint8_t other_frequency_flag:1; + uint8_t transmission_mode:2; + uint8_t guard_interval:2; + uint8_t code_rate_lp_stream:3; + uint32_t reserved_future_use2; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the DVB-T terrestrial delivery system descriptor + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function initializes and makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * Currently, no memory is allocated internally. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_terrestrial_delivery_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, + struct dvb_desc *desc); + +/** + * @brief Prints the content of the DVB-T terrestrial delivery system descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_terrestrial_delivery_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief converts from internal representation into bandwidth in Hz + */ +extern const unsigned dvbt_bw[]; + +/** + * @brief converts from the descriptor's modulation into enum fe_modulation, + * as defined by DVBv5 API. + */ +extern const unsigned dvbt_modulation[]; + +/** + * @brief converts from the descriptor's hierarchy into enum fe_hierarchy, + * as defined by DVBv5 API. + */ +extern const unsigned dvbt_hierarchy[]; + +/** + * @brief converts from the descriptor's FEC into enum fe_code_rate, + * as defined by DVBv5 API. + */ +extern const unsigned dvbt_code_rate[]; + +/** + * @brief converts from internal representation into enum fe_guard_interval, + * as defined at DVBv5 API. + */ +extern const uint32_t dvbt_interval[]; + +/** + * @brief converts from the descriptor's transmission mode into + * enum fe_transmit_mode, as defined by DVBv5 API. + */ +extern const unsigned dvbt_transmission_mode[]; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/desc_ts_info.h b/include/libdvbv5/desc_ts_info.h new file mode 100644 index 0000000..4e10c38 --- /dev/null +++ b/include/libdvbv5/desc_ts_info.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2013-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described on ARIB STD-B10 as TS information descriptor + */ + +/** + * @file desc_ts_info.h + * @ingroup descriptors + * @brief Provides the descriptors for the ISDB TS information descriptor. + * The TS information descriptor specifies the remote control key + * identifier assigned to the applicable TS and indicates the relationship + * between the service identifier and the transmission layer during + * hierarchical transmission. + * + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Relevant specs + * The descriptor described herein is defined at: + * - ARIB STD-B10 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _TS_INFO_H +#define _TS_INFO_H + +#include + +/** + * @struct dvb_desc_ts_info_transmission_type + * @ingroup descriptors + * @brief ISDB TS information transmission type + * + * @param transmission_type_info transmission type info + * @param num_of_service num of service + */ +struct dvb_desc_ts_info_transmission_type { + uint8_t transmission_type_info; + uint8_t num_of_service; +} __attribute__((packed)); + +/** + * @struct dvb_desc_ts_info + * @ingroup descriptors + * @brief Structure describing the ISDB TS information descriptor. + * + * @param type descriptor tag + * @param length descriptor length + * @param next pointer to struct dvb_desc + * @param remote_control_key_id remote control key id + * @param length_of_ts_name length of ts name + * @param transmission_type_count transmission type count + * + * @param ts_name ts name string + * @param ts_name_emph ts name emphasis string + * @param transmission_type struct dvb_desc_ts_info_transmission_type content + * @param service_id service id vector + */ +struct dvb_desc_ts_info { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + char *ts_name, *ts_name_emph; + struct dvb_desc_ts_info_transmission_type transmission_type; + uint16_t *service_id; + + union { + uint16_t bitfield; + struct { + uint8_t transmission_type_count:2; + uint8_t length_of_ts_name:6; + uint8_t remote_control_key_id:8; + } __attribute__((packed)); + }; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses the ISDB TS information descriptor. + * descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the descriptor's raw data + * @param desc pointer to struct dvb_desc to be allocated and filled + * + * This function allocates a the descriptor and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +int dvb_desc_ts_info_init(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Prints the content of the ISDB TS information descriptor. + * descriptor + * @ingroup descriptors + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param desc pointer to struct dvb_desc + */ +void dvb_desc_ts_info_print(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Frees all data allocated by the ISDB TS information descriptor. + * descriptor + * @ingroup descriptors + * + * @param desc pointer to struct dvb_desc to be freed + */ +void dvb_desc_ts_info_free(struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/descriptors.h b/include/libdvbv5/descriptors.h new file mode 100644 index 0000000..c12ffc7 --- /dev/null +++ b/include/libdvbv5/descriptors.h @@ -0,0 +1,772 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012-2014 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +/** + * @file descriptors.h + * @ingroup dvb_table + * @brief Provides a way to handle MPEG-TS descriptors found on Digital TV + * streams. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The descriptors herein are defined on the following specs: + * - ISO/IEC 13818-1 + * - ETSI EN 300 468 V1.11.1 (2010-04) + * - SCTE 35 2004 + * - http://www.etherguidesystems.com/Help/SDOs/ATSC/Semantics/Descriptors/Default.aspx + * - http://www.coolstf.com/tsreader/descriptors.html + * - ABNT NBR 15603-1 2007 + * - ATSC A/65:2009 spec + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + + +#ifndef _DESCRIPTORS_H +#define _DESCRIPTORS_H + +#include +#include +#include + +/** + * @brief Maximum size of a table session to be parsed + * @ingroup dvb_table + */ +#define DVB_MAX_PAYLOAD_PACKET_SIZE 4096 + +/** + * @brief number of bytes for the descriptor's CRC check + * @ingroup dvb_table + */ +#define DVB_CRC_SIZE 4 + + +#ifndef _DOXYGEN +struct dvb_v5_fe_parms; +#endif + +/** + * @brief Function prototype for a function that initializes the + * descriptors parsing on a table + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param buf Buffer with data to be parsed + * @param buflen Size of the buffer to be parsed + * @param table pointer to a place where the allocated memory with the + * table structure will be stored. + */ +typedef void (*dvb_table_init_func)(struct dvb_v5_fe_parms *parms, + const uint8_t *buf, ssize_t buflen, + void **table); + +/** + * @brief Table with all possible descriptors + * @ingroup dvb_table + */ +extern const dvb_table_init_func dvb_table_initializers[256]; + +#ifndef _DOXYGEN +#define bswap16(b) do {\ + b = ntohs(b); \ +} while (0) + +#define bswap32(b) do {\ + b = ntohl(b); \ +} while (0) + +/* Deprecated */ +#define DVB_DESC_HEADER() \ + uint8_t type; \ + uint8_t length; \ + struct dvb_desc *next + +#endif /* _DOXYGEN */ + +/** + * @struct dvb_desc + * @brief Linked list containing the several descriptors found on a + * MPEG-TS table + * @ingroup dvb_table + * + * @param type Descriptor type + * @param length Length of the descriptor + * @param next pointer to the dvb_desc descriptor + * @param data Descriptor data + */ +struct dvb_desc { + uint8_t type; + uint8_t length; + struct dvb_desc *next; + + uint8_t data[]; +} __attribute__((packed)); + +#ifndef _DOXYGEN + +#define dvb_desc_foreach( _desc, _tbl ) \ + for( struct dvb_desc *_desc = _tbl->descriptor; _desc; _desc = _desc->next ) \ + +#define dvb_desc_find(_struct, _desc, _tbl, _type) \ + for( _struct *_desc = (_struct *) _tbl->descriptor; _desc; _desc = (_struct *) _desc->next ) \ + if(_desc->type == _type) \ + +#endif /* _DOXYGEN */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Converts from BCD to CPU integer internal representation + * @ingroup dvb_table + * + * @param bcd value in BCD encoding + */ +uint32_t dvb_bcd(uint32_t bcd); + +/** + * @brief dumps data into the logs in hexadecimal format + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param prefix String to be printed before the dvb_hexdump + * @param buf Buffer to hex dump + * @param len Number of bytes to show + */ +void dvb_hexdump(struct dvb_v5_fe_parms *parms, const char *prefix, + const unsigned char *buf, int len); + +/** + * @brief parse MPEG-TS descriptors + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param buf Buffer with data to be parsed + * @param buflen Size of the buffer to be parsed + * @param head_desc pointer to the place to store the parsed data + * + * This function takes a buf as argument and parses it to find the + * MPEG-TS descriptors inside it, creating a linked list. + * + * On success, head_desc will be allocated and filled with a linked list + * with the descriptors found inside the buffer. + * + * This function is used by the several MPEG-TS table handlers to parse + * the entire table that got read by dvb_read_sessions and other similar + * functions. + * + * @return Returns 0 on success, a negative value otherwise. + */ +int dvb_desc_parse(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + uint16_t buflen, struct dvb_desc **head_desc); + +/** + * @brief frees a dvb_desc linked list + * @ingroup dvb_table + * + * @param list struct dvb_desc pointer. + */ +void dvb_desc_free (struct dvb_desc **list); + +/** + * @brief prints the contents of a struct dvb_desc linked list + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param desc struct dvb_desc pointer. + */ +void dvb_desc_print(struct dvb_v5_fe_parms *parms, struct dvb_desc *desc); + +#ifdef __cplusplus +} +#endif + +/** + * @brief Function prototype for the descriptors parsing init code + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param buf buffer with the content of the descriptor + * @param desc struct dvb_desc pointer + */ +typedef int (*dvb_desc_init_func) (struct dvb_v5_fe_parms *parms, + const uint8_t *buf, struct dvb_desc *desc); + +/** + * @brief Function prototype for the descriptors parsing print code + * @ingroup dvb_table + * + * @param parms Struct dvb_v5_fe_parms pointer + * @param desc struct dvb_desc pointer + */ +typedef void (*dvb_desc_print_func)(struct dvb_v5_fe_parms *parms, + const struct dvb_desc *desc); + +/** + * @brief Function prototype for the descriptors memory free code + * @ingroup dvb_table + * + * @param desc pointer to struct dvb_desc pointer to be freed + */ +typedef void (*dvb_desc_free_func) (struct dvb_desc *desc); + +/** + * @struct dvb_descriptor + * @brief Contains the parser information for the MPEG-TS parser code + * @ingroup dvb_table + * + * @param name String containing the name of the descriptor + * @param init Pointer to a function to initialize the descriptor + * parser. This function fills the descriptor-specific + * internal structures + * @param print Prints the content of the descriptor + * @param free Frees all memory blocks allocated by the init function + * @param size Descriptor's size, in bytes. + */ +struct dvb_descriptor { + const char *name; + dvb_desc_init_func init; + dvb_desc_print_func print; + dvb_desc_free_func free; + ssize_t size; +}; + +/** + * @brief Contains the parsers for the several descriptors + * @ingroup dvb_table + */ +extern const struct dvb_descriptor dvb_descriptors[]; + +/** + * @enum descriptors + * @brief List containing all descriptors used by Digital TV MPEG-TS + * @ingroup dvb_table + * + * @var video_stream_descriptor + * @brief video_stream descriptor - ISO/IEC 13818-1 + * @var audio_stream_descriptor + * @brief audio_stream descriptor - ISO/IEC 13818-1 + * @var hierarchy_descriptor + * @brief hierarchy descriptor - ISO/IEC 13818-1 + * @var registration_descriptor + * @brief registration descriptor - ISO/IEC 13818-1 + * @var ds_alignment_descriptor + * @brief ds_alignment descriptor - ISO/IEC 13818-1 + * @var target_background_grid_descriptor + * @brief target_background_grid descriptor - ISO/IEC 13818-1 + * @var video_window_descriptor + * @brief video_window descriptor - ISO/IEC 13818-1 + * @var conditional_access_descriptor + * @brief conditional_access descriptor - ISO/IEC 13818-1 + * @var iso639_language_descriptor + * @brief iso639_language descriptor - ISO/IEC 13818-1 + * @var system_clock_descriptor + * @brief system_clock descriptor - ISO/IEC 13818-1 + * @var multiplex_buffer_utilization_descriptor + * @brief multiplex_buffer_utilization descriptor - ISO/IEC 13818-1 + * @var copyright_descriptor + * @brief copyright descriptor - ISO/IEC 13818-1 + * @var maximum_bitrate_descriptor + * @brief maximum_bitrate descriptor - ISO/IEC 13818-1 + * @var private_data_indicator_descriptor + * @brief private_data_indicator descriptor - ISO/IEC 13818-1 + * @var smoothing_buffer_descriptor + * @brief smoothing_buffer descriptor - ISO/IEC 13818-1 + * @var std_descriptor + * @brief std descriptor - ISO/IEC 13818-1 + * @var ibp_descriptor + * @brief ibp descriptor - ISO/IEC 13818-1 + * @var mpeg4_video_descriptor + * @brief mpeg4_video descriptor - ISO/IEC 13818-1 + * @var mpeg4_audio_descriptor + * @brief mpeg4_audio descriptor - ISO/IEC 13818-1 + * @var iod_descriptor + * @brief iod descriptor - ISO/IEC 13818-1 + * @var sl_descriptor + * @brief sl descriptor - ISO/IEC 13818-1 + * @var fmc_descriptor + * @brief fmc descriptor - ISO/IEC 13818-1 + * @var external_es_id_descriptor + * @brief external_es_id descriptor - ISO/IEC 13818-1 + * @var muxcode_descriptor + * @brief muxcode descriptor - ISO/IEC 13818-1 + * @var fmxbuffersize_descriptor + * @brief fmxbuffersize descriptor - ISO/IEC 13818-1 + * @var multiplexbuffer_descriptor + * @brief multiplexbuffer descriptor - ISO/IEC 13818-1 + * @var content_labeling_descriptor + * @brief content_labeling descriptor - ISO/IEC 13818-1 + * @var metadata_pointer_descriptor + * @brief metadata_pointer descriptor - ISO/IEC 13818-1 + * @var metadata_descriptor + * @brief metadata descriptor - ISO/IEC 13818-1 + * @var metadata_std_descriptor + * @brief metadata_std descriptor - ISO/IEC 13818-1 + * @var AVC_video_descriptor + * @brief AVC_video descriptor - ISO/IEC 13818-1 + * @var ipmp_descriptor + * @brief ipmp descriptor - ISO/IEC 13818-1 + * @var AVC_timing_and_HRD_descriptor + * @brief AVC_timing_and_HRD descriptor - ISO/IEC 13818-1 + * @var mpeg2_aac_audio_descriptor + * @brief mpeg2_aac_audio descriptor - ISO/IEC 13818-1 + * @var flexmux_timing_descriptor + * @brief flexmux_timing descriptor - ISO/IEC 13818-1 + * @var network_name_descriptor + * @brief network_name descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var service_list_descriptor + * @brief service_list descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var stuffing_descriptor + * @brief stuffing descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var satellite_delivery_system_descriptor + * @brief satellite_delivery_system descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var cable_delivery_system_descriptor + * @brief cable_delivery_system descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var VBI_data_descriptor + * @brief VBI_data descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var VBI_teletext_descriptor + * @brief VBI_teletext descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var bouquet_name_descriptor + * @brief bouquet_name descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var service_descriptor + * @brief service descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var country_availability_descriptor + * @brief country_availability descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var linkage_descriptor + * @brief linkage descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var NVOD_reference_descriptor + * @brief NVOD_reference descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var time_shifted_service_descriptor + * @brief time_shifted_service descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var short_event_descriptor + * @brief short_event descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var extended_event_descriptor + * @brief extended_event descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var time_shifted_event_descriptor + * @brief time_shifted_event descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var component_descriptor + * @brief component descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var mosaic_descriptor + * @brief mosaic descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var stream_identifier_descriptor + * @brief stream_identifier descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var CA_identifier_descriptor + * @brief CA_identifier descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var content_descriptor + * @brief content descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var parental_rating_descriptor + * @brief parental_rating descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var teletext_descriptor + * @brief teletext descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var telephone_descriptor + * @brief telephone descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var local_time_offset_descriptor + * @brief local_time_offset descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var subtitling_descriptor + * @brief subtitling descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var terrestrial_delivery_system_descriptor + * @brief terrestrial_delivery_system descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var multilingual_network_name_descriptor + * @brief multilingual_network_name descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var multilingual_bouquet_name_descriptor + * @brief multilingual_bouquet_name descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var multilingual_service_name_descriptor + * @brief multilingual_service_name descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var multilingual_component_descriptor + * @brief multilingual_component descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var private_data_specifier_descriptor + * @brief private_data_specifier descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var service_move_descriptor + * @brief service_move descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var short_smoothing_buffer_descriptor + * @brief short_smoothing_buffer descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var frequency_list_descriptor + * @brief frequency_list descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var partial_transport_stream_descriptor + * @brief partial_transport_stream descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var data_broadcast_descriptor + * @brief data_broadcast descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var scrambling_descriptor + * @brief scrambling descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var data_broadcast_id_descriptor + * @brief data_broadcast_id descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var transport_stream_descriptor + * @brief transport_stream descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var DSNG_descriptor + * @brief DSNG descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var PDC_descriptor + * @brief PDC descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var AC_3_descriptor + * @brief AC_3 descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var ancillary_data_descriptor + * @brief ancillary_data descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var cell_list_descriptor + * @brief cell_list descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var cell_frequency_link_descriptor + * @brief cell_frequency_link descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var announcement_support_descriptor + * @brief announcement_support descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var application_signalling_descriptor + * @brief application_signalling descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var adaptation_field_data_descriptor + * @brief adaptation_field_data descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var service_identifier_descriptor + * @brief service_identifier descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var service_availability_descriptor + * @brief service_availability descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var default_authority_descriptor + * @brief default_authority descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var related_content_descriptor + * @brief related_content descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var TVA_id_descriptor + * @brief TVA_id descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var content_identifier_descriptor + * @brief content_identifier descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var time_slice_fec_identifier_descriptor + * @brief time_slice_fec_identifier descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var ECM_repetition_rate_descriptor + * @brief ECM_repetition_rate descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var S2_satellite_delivery_system_descriptor + * @brief S2_satellite_delivery_system descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var enhanced_AC_3_descriptor + * @brief enhanced_AC_3 descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var DTS_descriptor + * @brief DTS descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var AAC_descriptor + * @brief AAC descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var XAIT_location_descriptor + * @brief XAIT_location descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var FTA_content_management_descriptor + * @brief FTA_content_management descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var extension_descriptor + * @brief extension descriptor - ETSI EN 300 468 V1.11.1 (2010-04) + * @var CUE_identifier_descriptor + * @brief CUE_identifier descriptor - SCTE 35 2004 + * @var extended_channel_name + * @brief extended_channel_name descriptor - SCTE 35 2004 + * @var service_location + * @brief service_location descriptor - SCTE 35 2004 + * @var component_name_descriptor + * @brief component_name descriptor - SCTE 35 2004 + * @see http://www.etherguidesystems.com/Help/SDOs/ATSC/Semantics/Descriptors/Default.aspx + * @var logical_channel_number_descriptor + * @brief logical_channel_number descriptor - SCTE 35 2004 + * @see http://www.coolstf.com/tsreader/descriptors.html + * + * @var carousel_id_descriptor + * @brief carousel_id descriptor - ABNT NBR 15603-1 2007 + * @var association_tag_descriptor + * @brief association_tag descriptor - ABNT NBR 15603-1 2007 + * @var deferred_association_tags_descriptor + * @brief deferred_association_tags descriptor - ABNT NBR 15603-1 2007 + + * @var hierarchical_transmission_descriptor + * @brief hierarchical_transmission descriptor - ABNT NBR 15603-1 2007 + * @var digital_copy_control_descriptor + * @brief digital_copy_control descriptor - ABNT NBR 15603-1 2007 + * @var network_identifier_descriptor + * @brief network_identifier descriptor - ABNT NBR 15603-1 2007 + * @var partial_transport_stream_time_descriptor + * @brief partial_transport_stream_time descriptor - ABNT NBR 15603-1 2007 + * @var audio_component_descriptor + * @brief audio_component descriptor - ABNT NBR 15603-1 2007 + * @var hyperlink_descriptor + * @brief hyperlink descriptor - ABNT NBR 15603-1 2007 + * @var target_area_descriptor + * @brief target_area descriptor - ABNT NBR 15603-1 2007 + * @var data_contents_descriptor + * @brief data_contents descriptor - ABNT NBR 15603-1 2007 + * @var video_decode_control_descriptor + * @brief video_decode_control descriptor - ABNT NBR 15603-1 2007 + * @var download_content_descriptor + * @brief download_content descriptor - ABNT NBR 15603-1 2007 + * @var CA_EMM_TS_descriptor + * @brief CA_EMM_TS descriptor - ABNT NBR 15603-1 2007 + * @var CA_contract_information_descriptor + * @brief CA_contract_information descriptor - ABNT NBR 15603-1 2007 + * @var CA_service_descriptor + * @brief CA_service descriptor - ABNT NBR 15603-1 2007 + * @var TS_Information_descriptior + * @brief transport_stream_information descriptor - ABNT NBR 15603-1 2007 + * @var extended_broadcaster_descriptor + * @brief extended_broadcaster descriptor - ABNT NBR 15603-1 2007 + * @var logo_transmission_descriptor + * @brief logo_transmission descriptor - ABNT NBR 15603-1 2007 + * @var basic_local_event_descriptor + * @brief basic_local_event descriptor - ABNT NBR 15603-1 2007 + * @var reference_descriptor + * @brief reference descriptor - ABNT NBR 15603-1 2007 + * @var node_relation_descriptor + * @brief node_relation descriptor - ABNT NBR 15603-1 2007 + * @var short_node_information_descriptor + * @brief short_node_information descriptor - ABNT NBR 15603-1 2007 + * @var STC_reference_descriptor + * @brief STC_reference descriptor - ABNT NBR 15603-1 2007 + * @var series_descriptor + * @brief series descriptor - ABNT NBR 15603-1 2007 + * @var event_group_descriptor + * @brief event_group descriptor - ABNT NBR 15603-1 2007 + * @var SI_parameter_descriptor + * @brief SI_parameter descriptor - ABNT NBR 15603-1 2007 + * @var broadcaster_Name_Descriptor + * @brief broadcaster_Name descriptor - ABNT NBR 15603-1 2007 + * @var component_group_descriptor + * @brief component_group descriptor - ABNT NBR 15603-1 2007 + * @var SI_prime_TS_descriptor + * @brief SI_prime_transport_stream descriptor - ABNT NBR 15603-1 2007 + * @var board_information_descriptor + * @brief board_information descriptor - ABNT NBR 15603-1 2007 + * @var LDT_linkage_descriptor + * @brief LDT_linkage descriptor - ABNT NBR 15603-1 2007 + * @var connected_transmission_descriptor + * @brief connected_transmission descriptor - ABNT NBR 15603-1 2007 + * @var content_availability_descriptor + * @brief content_availability descriptor - ABNT NBR 15603-1 2007 + * @var service_group_descriptor + * @brief service_group descriptor - ABNT NBR 15603-1 2007 + * @var carousel_compatible_composite_descriptor + * @brief carousel_compatible_composite descriptor - ABNT NBR 15603-1 2007 + * @var conditional_playback_descriptor + * @brief conditional_playback descriptor - ABNT NBR 15603-1 2007 + * @var ISDBT_delivery_system_descriptor + * @brief ISDBT terrestrial_delivery_system descriptor - ABNT NBR 15603-1 2007 + * @var partial_reception_descriptor + * @brief partial_reception descriptor - ABNT NBR 15603-1 2007 + * @var emergency_information_descriptor + * @brief emergency_information descriptor - ABNT NBR 15603-1 2007 + * @var data_component_descriptor + * @brief data_component descriptor - ABNT NBR 15603-1 2007 + * @var system_management_descriptor + * @brief system_management descriptor - ABNT NBR 15603-1 2007 + * + * @var atsc_stuffing_descriptor + * @brief atsc_stuffing descriptor - ATSC A/65:2009 + * @var atsc_ac3_audio_descriptor + * @brief atsc_ac3_audio descriptor - ATSC A/65:2009 + * @var atsc_caption_service_descriptor + * @brief atsc_caption_service descriptor - ATSC A/65:2009 + * @var atsc_content_advisory_descriptor + * @brief atsc_content_advisory descriptor - ATSC A/65:2009 + * @var atsc_extended_channel_descriptor + * @brief atsc_extended_channel descriptor - ATSC A/65:2009 + * @var atsc_service_location_descriptor + * @brief atsc_service_location descriptor - ATSC A/65:2009 + * @var atsc_time_shifted_service_descriptor + * @brief atsc_time_shifted_service descriptor - ATSC A/65:2009 + * @var atsc_component_name_descriptor + * @brief atsc_component_name descriptor - ATSC A/65:2009 + * @var atsc_DCC_departing_request_descriptor + * @brief atsc_DCC_departing_request descriptor - ATSC A/65:2009 + * @var atsc_DCC_arriving_request_descriptor + * @brief atsc_DCC_arriving_request descriptor - ATSC A/65:2009 + * @var atsc_redistribution_control_descriptor + * @brief atsc_redistribution_control descriptor - ATSC A/65:2009 + * @var atsc_ATSC_private_information_descriptor + * @brief atsc_ATSC_private_information descriptor - ATSC A/65:2009 + * @var atsc_genre_descriptor + * @brief atsc_genre descriptor - ATSC A/65:2009 + */ +enum descriptors { + /* ISO/IEC 13818-1 */ + video_stream_descriptor = 0x02, + audio_stream_descriptor = 0x03, + hierarchy_descriptor = 0x04, + registration_descriptor = 0x05, + ds_alignment_descriptor = 0x06, + target_background_grid_descriptor = 0x07, + video_window_descriptor = 0x08, + conditional_access_descriptor = 0x09, + iso639_language_descriptor = 0x0a, + system_clock_descriptor = 0x0b, + multiplex_buffer_utilization_descriptor = 0x0c, + copyright_descriptor = 0x0d, + maximum_bitrate_descriptor = 0x0e, + private_data_indicator_descriptor = 0x0f, + smoothing_buffer_descriptor = 0x10, + std_descriptor = 0x11, + ibp_descriptor = 0x12, + + mpeg4_video_descriptor = 0x1b, + mpeg4_audio_descriptor = 0x1c, + iod_descriptor = 0x1d, + sl_descriptor = 0x1e, + fmc_descriptor = 0x1f, + external_es_id_descriptor = 0x20, + muxcode_descriptor = 0x21, + fmxbuffersize_descriptor = 0x22, + multiplexbuffer_descriptor = 0x23, + content_labeling_descriptor = 0x24, + metadata_pointer_descriptor = 0x25, + metadata_descriptor = 0x26, + metadata_std_descriptor = 0x27, + AVC_video_descriptor = 0x28, + ipmp_descriptor = 0x29, + AVC_timing_and_HRD_descriptor = 0x2a, + mpeg2_aac_audio_descriptor = 0x2b, + flexmux_timing_descriptor = 0x2c, + + /* ETSI EN 300 468 V1.11.1 (2010-04) */ + + network_name_descriptor = 0x40, + service_list_descriptor = 0x41, + stuffing_descriptor = 0x42, + satellite_delivery_system_descriptor = 0x43, + cable_delivery_system_descriptor = 0x44, + VBI_data_descriptor = 0x45, + VBI_teletext_descriptor = 0x46, + bouquet_name_descriptor = 0x47, + service_descriptor = 0x48, + country_availability_descriptor = 0x49, + linkage_descriptor = 0x4a, + NVOD_reference_descriptor = 0x4b, + time_shifted_service_descriptor = 0x4c, + short_event_descriptor = 0x4d, + extended_event_descriptor = 0x4e, + time_shifted_event_descriptor = 0x4f, + component_descriptor = 0x50, + mosaic_descriptor = 0x51, + stream_identifier_descriptor = 0x52, + CA_identifier_descriptor = 0x53, + content_descriptor = 0x54, + parental_rating_descriptor = 0x55, + teletext_descriptor = 0x56, + telephone_descriptor = 0x57, + local_time_offset_descriptor = 0x58, + subtitling_descriptor = 0x59, + terrestrial_delivery_system_descriptor = 0x5a, + multilingual_network_name_descriptor = 0x5b, + multilingual_bouquet_name_descriptor = 0x5c, + multilingual_service_name_descriptor = 0x5d, + multilingual_component_descriptor = 0x5e, + private_data_specifier_descriptor = 0x5f, + service_move_descriptor = 0x60, + short_smoothing_buffer_descriptor = 0x61, + frequency_list_descriptor = 0x62, + partial_transport_stream_descriptor = 0x63, + data_broadcast_descriptor = 0x64, + scrambling_descriptor = 0x65, + data_broadcast_id_descriptor = 0x66, + transport_stream_descriptor = 0x67, + DSNG_descriptor = 0x68, + PDC_descriptor = 0x69, + AC_3_descriptor = 0x6a, + ancillary_data_descriptor = 0x6b, + cell_list_descriptor = 0x6c, + cell_frequency_link_descriptor = 0x6d, + announcement_support_descriptor = 0x6e, + application_signalling_descriptor = 0x6f, + adaptation_field_data_descriptor = 0x70, + service_identifier_descriptor = 0x71, + service_availability_descriptor = 0x72, + default_authority_descriptor = 0x73, + related_content_descriptor = 0x74, + TVA_id_descriptor = 0x75, + content_identifier_descriptor = 0x76, + time_slice_fec_identifier_descriptor = 0x77, + ECM_repetition_rate_descriptor = 0x78, + S2_satellite_delivery_system_descriptor = 0x79, + enhanced_AC_3_descriptor = 0x7a, + DTS_descriptor = 0x7b, + AAC_descriptor = 0x7c, + XAIT_location_descriptor = 0x7d, + FTA_content_management_descriptor = 0x7e, + extension_descriptor = 0x7f, + + /* SCTE 35 2004 */ + CUE_identifier_descriptor = 0x8a, + + extended_channel_name = 0xa0, + service_location = 0xa1, + /* From http://www.etherguidesystems.com/Help/SDOs/ATSC/Semantics/Descriptors/Default.aspx */ + component_name_descriptor = 0xa3, + + /* From http://www.coolstf.com/tsreader/descriptors.html */ + logical_channel_number_descriptor = 0x83, + + /* ISDB Descriptors, as defined on ABNT NBR 15603-1 2007 */ + + carousel_id_descriptor = 0x13, + association_tag_descriptor = 0x14, + deferred_association_tags_descriptor = 0x15, + + hierarchical_transmission_descriptor = 0xc0, + digital_copy_control_descriptor = 0xc1, + network_identifier_descriptor = 0xc2, + partial_transport_stream_time_descriptor = 0xc3, + audio_component_descriptor = 0xc4, + hyperlink_descriptor = 0xc5, + target_area_descriptor = 0xc6, + data_contents_descriptor = 0xc7, + video_decode_control_descriptor = 0xc8, + download_content_descriptor = 0xc9, + CA_EMM_TS_descriptor = 0xca, + CA_contract_information_descriptor = 0xcb, + CA_service_descriptor = 0xcc, + TS_Information_descriptior = 0xcd, + extended_broadcaster_descriptor = 0xce, + logo_transmission_descriptor = 0xcf, + basic_local_event_descriptor = 0xd0, + reference_descriptor = 0xd1, + node_relation_descriptor = 0xd2, + short_node_information_descriptor = 0xd3, + STC_reference_descriptor = 0xd4, + series_descriptor = 0xd5, + event_group_descriptor = 0xd6, + SI_parameter_descriptor = 0xd7, + broadcaster_Name_Descriptor = 0xd8, + component_group_descriptor = 0xd9, + SI_prime_TS_descriptor = 0xda, + board_information_descriptor = 0xdb, + LDT_linkage_descriptor = 0xdc, + connected_transmission_descriptor = 0xdd, + content_availability_descriptor = 0xde, + service_group_descriptor = 0xe0, + carousel_compatible_composite_descriptor = 0xf7, + conditional_playback_descriptor = 0xf8, + ISDBT_delivery_system_descriptor = 0xfa, + partial_reception_descriptor = 0xfb, + emergency_information_descriptor = 0xfc, + data_component_descriptor = 0xfd, + system_management_descriptor = 0xfe, + + /* ATSC descriptors - ATSC A/65:2009 spec */ + atsc_stuffing_descriptor = 0x80, + atsc_ac3_audio_descriptor = 0x81, + atsc_caption_service_descriptor = 0x86, + atsc_content_advisory_descriptor = 0x87, + atsc_extended_channel_descriptor = 0xa0, + atsc_service_location_descriptor = 0xa1, + atsc_time_shifted_service_descriptor = 0xa2, + atsc_component_name_descriptor = 0xa3, + atsc_DCC_departing_request_descriptor = 0xa8, + atsc_DCC_arriving_request_descriptor = 0xa9, + atsc_redistribution_control_descriptor = 0xaa, + atsc_ATSC_private_information_descriptor = 0xad, + atsc_genre_descriptor = 0xab, +}; + +/* Please see desc_extension.h for extension_descriptor types */ + +#endif diff --git a/include/libdvbv5/dvb-demux.h b/include/libdvbv5/dvb-demux.h new file mode 100644 index 0000000..fd5b06c --- /dev/null +++ b/include/libdvbv5/dvb-demux.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * These routines were originally written as part of the dvb-apps, as: + * util functions for various ?zap implementations + * + * Copyright (C) 2001 Johannes Stezenbach (js@convergence.de) + * for convergence integrated media + * + * Originally licensed as GPLv2 or upper + */ + +/** + * @file dvb-demux.h + * @ingroup demux + * @brief Provides interfaces to deal with DVB demux. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _DVB_DEMUX_H +#define _DVB_DEMUX_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Opens a DVB demux in read/write mode + * @ingroup demux + * + * @param adapter DVB adapter number to open + * @param demux DVB demux number to open + * + * @details This is a wrapper function to open. File is always opened in blocking mode. + * + * @return Returns a file descriptor on success, -1 otherwise. + */ +int dvb_dmx_open(int adapter, int demux); + +/** + * @brief Stops the DMX filter for the file descriptor and closes + * @ingroup demux + * + * @param dmx_fd File descriptor to close + * + * This is a wrapper function to open. + */ +void dvb_dmx_close(int dmx_fd); + +/** + * @brief Stops the DMX filter for a given file descriptor + * @ingroup demux + * + * @param dmx_fd File descriptor to close + * + * This is a wrapper function to open. + */ +void dvb_dmx_stop(int dmx_fd); + +/** + * @brief Start a filter for a MPEG-TS Packetized Elementary + * Stream (PES) + * @ingroup demux + * + * @param dmxfd File descriptor for the demux device + * @param pid Program ID to filter. Use 0x2000 to select all PIDs + * @param type type of the PID (DMX_PES_VIDEO, DMX_PES_AUDIO, + * DMX_PES_OTHER, etc). + * @param output Where the data will be output (DMX_OUT_TS_TAP, + * DMX_OUT_DECODER, etc). + * @param buffersize Size of the buffer to be allocated to store the filtered data. + * + * This is a wrapper function for DMX_SET_PES_FILTER ioctl. + * See http://linuxtv.org/downloads/v4l-dvb-apis/dvb_demux.html + * for more details. + * + * @return Retuns zero on success, -1 otherwise. + */ +int dvb_set_pesfilter(int dmxfd, int pid, dmx_pes_type_t type, + dmx_output_t output, int buffersize); + +/** + * @brief Sets a MPEG-TS section filter + * @ingroup demux + * + * @param dmxfd File descriptor for the demux device + * @param pid Program ID to filter. Use 0x2000 to select all PIDs + * @param filtsize Size of the filter (up to 18 btyes) + * @param filter data to filter. Can be NULL or should have filtsize length + * @param mask filter mask. Can be NULL or should have filtsize length + * @param mode mode mask. Can be NULL or should have filtsize length + * @param flags flags for set filter (DMX_CHECK_CRC,DMX_ONESHOT, + * DMX_IMMEDIATE_START). + * + * This is a wrapper function for DMX_SET_FILTER ioctl. + * See http://linuxtv.org/downloads/v4l-dvb-apis/dvb_demux.html + * for more details. + * + * @return Retuns zero on success, -1 otherwise. + */ +int dvb_set_section_filter(int dmxfd, int pid, unsigned filtsize, + unsigned char *filter, + unsigned char *mask, + unsigned char *mode, + unsigned int flags); + +/** + * @brief read the contents of the MPEG-TS PAT table, seeking for + * an specific service ID + * @ingroup demux + * + * @param dmxfd File descriptor for the demux device + * @param sid Session ID to seeking + * + * @warning This function currently assumes that the PAT fits into one session. + * + * @return At return, it returns a negative value if error or the PID associated with + * the desired Session ID. + */ +int dvb_get_pmt_pid(int dmxfd, int sid); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/dvb-fe.h b/include/libdvbv5/dvb-fe.h new file mode 100644 index 0000000..43fc282 --- /dev/null +++ b/include/libdvbv5/dvb-fe.h @@ -0,0 +1,771 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ +#ifndef _DVB_FE_H +#define _DVB_FE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dvb-frontend.h" +#include "dvb-sat.h" +#include "dvb-log.h" + +/** + * @file dvb-fe.h + * @ingroup frontend + * @brief Provides interfaces to deal with DVB frontend. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * The libdvbv5 API works with a set of key/value properties. + * There are two types of properties: + * + * - The ones defined at the Kernel's frontent API, that are found at + * /usr/include/linux/dvb/frontend.h (actually, it uses a local copy + * of that file, stored at ./include/linux/dvb/frontend.h) + * + * - Some extra properties used by libdvbv5. Those can be found at + * lib/include/libdvbv5/dvb-v5-std.h and start at DTV_USER_COMMAND_START. + * + * Just like the DTV properties, the stats are cached. That warrants that + * all stats are got at the same time, when dvb_fe_get_stats() is called. + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/** + * @def ARRAY_SIZE(array) + * @brief Calculates the number of elements of an array + * @ingroup ancillary + */ +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +/** + * @def MAX_DELIVERY_SYSTEMS + * @brief Max number of delivery systems for a given frontend. + * @ingroup frontend + */ +#define MAX_DELIVERY_SYSTEMS 20 + +#ifndef _DOXYGEN +/* + * There are a few aliases for other properties. Those are needed just + * to avoid breaking apps that depend on the library but shoudn't be used + * anymore on newer apps. + */ +#define DTV_MAX_STATS DTV_NUM_STATS_PROPS +#define DTV_SIGNAL_STRENGTH DTV_STAT_SIGNAL_STRENGTH +#define DTV_SNR DTV_STAT_CNR +#define DTV_UNCORRECTED_BLOCKS DTV_STAT_ERROR_BLOCK_COUNT + +#endif + +/** + * @struct dvb_v5_fe_parms + * @ingroup frontend + * @brief Keeps data needed to handle the DVB frontend + * + * @param info Contains the DVB info properties (RO) + * @param version Version of the Linux DVB API (RO) + * @param has_v5_stats A value different than 0 indicates that the + * frontend supports DVBv5 stats (RO) + * @param current_sys Currently selected delivery system (RO) + * @param num_systems Number of delivery systems (RO) + * @param systems Delivery systems supported by the hardware (RO) + * @param legacy_fe A value different than 0 indicates a legacy + * Kernel driver using DVBv3 API only, or that + * DVBv3 only mode was forced by the client (RO) + * @param abort Client should set it to abort a pending + * operation like DTV scan (RW) + * @param lna: Sets the LNA mode 0 disables; 1 enables, -1 uses + * auto mode (RW) + * @param lnb LNBf description (RW) + * @param sat_number Number of the satellite (used by DISEqC setup) (RW) + * @param freq_bpf SCR/Unicable band-pass filter frequency to use, in kHz + * @param verbose Verbosity level of the library (RW) + * @param dvb_logfunc Function used to write log messages (RO) + * @param default_charset Name of the charset used by the DVB standard (RW) + * @param output_charset Name of the charset to output (system specific) (RW) + * + * @details The fields marked as RO should not be changed by the client, as otherwise + * undesired effects may happen. The ones marked as RW are ok to either read + * or write by the client. + */ +struct dvb_v5_fe_parms { + /* Information visible to the client - don't override those values */ + struct dvb_frontend_info info; + uint32_t version; + int has_v5_stats; + fe_delivery_system_t current_sys; + int num_systems; + fe_delivery_system_t systems[MAX_DELIVERY_SYSTEMS]; + int legacy_fe; + + /* The values below are specified by the library client */ + + /* Flags from the client to the library */ + int abort; + + /* Linear Amplifier settings */ + int lna; + + /* Satellite settings */ + const struct dvb_sat_lnb *lnb; + int sat_number; + unsigned freq_bpf; + unsigned diseqc_wait; + + /* Function to write DVB logs */ + unsigned verbose; + dvb_logfunc logfunc; + + /* Charsets to be used by the conversion utilities */ + char *default_charset; + char *output_charset; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Allocates a dummy frontend structure + * @ingroup frontend + * + * @details This is useful for some applications that may want to just use the + * frontend structure internally, without associating it with a real hardware + * + * @return Returns a pointer to a dummy struct, or NULL if no memory. + */ +struct dvb_v5_fe_parms *dvb_fe_dummy(void); + +/** + * @brief Opens a frontend and allocates a structure to work with + * @ingroup frontend + * + * @param adapter Number of the adapter to open + * @param frontend Number of the frontend to open + * @param verbose Verbosity level of the messages that will be + * printed + * @param use_legacy_call Force to use the DVBv3 calls, instead of using + * the DVBv5 API + * @param logfunc Callback function to be called when a log event + * happens. Can either store the event into a file + * or to print it at the TUI/GUI. If NULL, the + * library will use its internal handler. + * @param flags Flags to be passed to open. Currently only two + * flags are supported: O_RDONLY or O_RDWR. + * Using O_NONBLOCK may hit unexpected issues. + * + * @todo Add/check support for O_NONBLOCK at the scan routines. + * + * @details This function should be called before using any other function at + * the frontend library (or the other alternatives: dvb_fe_open() or + * dvb_fe_dummy(). + * + * In general, this is called using O_RDWR, except if all that it is wanted + * is to check the DVB frontend statistics. + * + * @return Returns a pointer to an allocated data pointer or NULL on error. + */ +struct dvb_v5_fe_parms *dvb_fe_open_flags(int adapter, int frontend, + unsigned verbose, + unsigned use_legacy_call, + dvb_logfunc logfunc, + int flags); + +/** + * @brief Opens a frontend and allocates a structure to work with + * @ingroup frontend + * + * @param adapter Number of the adapter to open + * @param frontend Number of the frontend to open + * @param verbose Verbosity level of the messages that will be + * printed + * @param use_legacy_call Force to use the DVBv3 calls, instead of using + * the DVBv5 API + * + * @details This function should be called before using any other function at + * the frontend library (or the other alternatives: dvb_fe_open2() or + * dvb_fe_dummy(). + * + * @return Returns a pointer to an allocated data pointer or NULL on error. + */ +struct dvb_v5_fe_parms *dvb_fe_open(int adapter, int frontend, + unsigned verbose, + unsigned use_legacy_call); + +/** + * @brief Opens a frontend and allocates a structure to work with + * @ingroup frontend + * + * @param adapter Number of the adapter to open + * @param frontend Number of the frontend to open + * @param verbose Verbosity level of the messages that will be + * printed + * @param use_legacy_call Force to use the DVBv3 calls, instead of using + * the DVBv5 API + * @param logfunc Callback function to be called when a log event + * happens. Can either store the event into a file + * or to print it at the TUI/GUI. + * + * @details This function should be called before using any other function at + * the frontend library (or the other alternatives: dvb_fe_open() or + * dvb_fe_dummy(). + * + * @return Returns a pointer to an allocated data pointer or NULL on error. + */ +struct dvb_v5_fe_parms *dvb_fe_open2(int adapter, int frontend, + unsigned verbose, unsigned use_legacy_call, + dvb_logfunc logfunc); + +/** + * @brief Closes the frontend and frees allocated resources + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + */ +void dvb_fe_close(struct dvb_v5_fe_parms *parms); + +/** + * @brief Returns the string name associated with a DVBv5 command + * @ingroup frontend + * + * @param cmd DVBv5 or libdvbv5 property + * + * @details This function gets an integer argument (cmd) and returns a string + * that corresponds to the name of that property. + * + * @return it returns a string that corresponds to the property name. + * For example: + * dvb_cmd_name(DTV_GUARD_INTERVAL) would return "GUARD_INTERVAL" + * It also returns names for the properties used internally by libdvbv5. + */ +const char *dvb_cmd_name(int cmd); + +/** + * @brief Returns an string array with the valid string values associated with a DVBv5 command + * @ingroup frontend + * + * @param cmd DVBv5 or libdvbv5 property + * + * @return it returns a string array that corresponds to the names associated + * with the possible values for that property, when available. + * For example: + * dvb_cmd_name(DTV_CODE_RATE_HP) would return an array with the + * possible values for the code rates: + * { "1/2", "2/3", ... NULL } + * @note The array always ends with NULL. + */ +const char *const *dvb_attr_names(int cmd); + +/* Get/set delivery system parameters */ + +/** + * @brief Retrieves the value of a DVBv5/libdvbv5 property + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param cmd DVBv5 or libdvbv5 property + * @param value Pointer to an uint32_t where the value will be stored. + * + * This reads the value of a property stored at the cache. Before using it, + * a dvb_fe_get_parms() is likely required. + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_fe_retrieve_parm(const struct dvb_v5_fe_parms *parms, + unsigned cmd, uint32_t *value); + +/** + * @brief Stores the value of a DVBv5/libdvbv5 property + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param cmd DVBv5 or libdvbv5 property + * @param value Pointer to an uint32_t where the value will be stored. + * + * This stores the value of a property at the cache. The value will only + * be send to the hardware after calling dvb_fe_set_parms(). + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_fe_store_parm(struct dvb_v5_fe_parms *parms, + unsigned cmd, uint32_t value); + +/** + * @brief Sets the delivery system + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param sys delivery system to be selected + * + * This function changes the delivery system of the frontend. By default, + * the libdvbv5 will use the first available delivery system. If another + * delivery system is desirable, this function should be called before being + * able to store the properties for the new delivery system via + * dvb_fe_store_parm(). + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_set_sys(struct dvb_v5_fe_parms *parms, + fe_delivery_system_t sys); + +/** + * @brief Make dvb properties reflect the current standard + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param sys delivery system to be selected + * + * This function prepares the properties cache for a given delivery system. + * + * It is automatically called by dvb_set_sys(), and should not be normally + * called, except when dvb_fe_dummy() is used. + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_add_parms_for_sys(struct dvb_v5_fe_parms *parms, + fe_delivery_system_t sys); + +/** + * @brief Sets the delivery system + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened + * device + * @param desired_system delivery system to be selected + * + * This function changes the delivery system of the frontend. By default, + * the libdvbv5 will use the first available delivery system. If another + * delivery system is desirable, this function should be called before being + * able to store the properties for the new delivery system via + * dvb_fe_store_parm(). + * + * This function is an enhanced version of dvb_set_sys(). It has an special + * logic inside to work with Kernels that supports only DVBv3. + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_set_compat_delivery_system(struct dvb_v5_fe_parms *parms, + uint32_t desired_system); + +/** + * @brief Prints all the properties at the cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * + * Used mostly for debugging issues. + */ +void dvb_fe_prt_parms(const struct dvb_v5_fe_parms *parms); + +/** + * @brief Prints all the properties at the cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * + * Writes the properties stored at the DVB cache at the DVB hardware. At + * return, some properties could have a different value, as the frontend + * may not support the values set. + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_fe_set_parms(struct dvb_v5_fe_parms *parms); + +/** + * @brief Prints all the properties at the cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * + * Gets the properties from the DVB hardware. The values will only reflect + * what's set at the hardware if the frontend is locked. + * + * @return Return 0 if success, EINVAL otherwise. + */ +int dvb_fe_get_parms(struct dvb_v5_fe_parms *parms); + +/* + * statistics functions + */ + +/** + * @brief Retrieve the stats for a DTV layer from cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param cmd DVBv5 or libdvbv5 property + * @param layer DTV layer + * + * Gets the value for one stats cache, on a given layer. Layer 0 is + * always present. On DTV standards that doesn't have layers, it returns + * the same value as dvb_fe_retrieve_stats() for layer = 0. + * + * For DTV standards with multiple layers, like ISDB, layer=1 is layer 'A', + * layer=2 is layer 'B' and layer=3 is layer 'C'. Please notice that not all + * frontends support per-layer stats. Also, the layer value is only valid if + * the layer exists at the original stream. + * Also, on such standards, layer 0 is typically a mean value of the layers, + * or a sum of events (if FE_SCALE_COUNTER). + * + * For it to be valid, dvb_fe_get_stats() should be called first. + * + * @return It returns a struct dtv_stats if succeed or NULL otherwise. + */ +struct dtv_stats *dvb_fe_retrieve_stats_layer(struct dvb_v5_fe_parms *parms, + unsigned cmd, unsigned layer); + +/** + * @brief Retrieve the stats for a DTV layer from cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param cmd DVBv5 or libdvbv5 property + * @param value DTV value pointer + * + * Gets the value for one stats property for layer = 0. + * + * For it to be valid, dvb_fe_get_stats() should be called first. + * + * @return The returned value is 0 if success, EINVAL otherwise. + */ +int dvb_fe_retrieve_stats(struct dvb_v5_fe_parms *parms, + unsigned cmd, uint32_t *value); + +/** + * @brief Retrieve the stats from the Kernel + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * + * Updates the stats cache from the available stats at the Kernel. + * + * @return The returned value is 0 if success, EINVAL otherwise. + */ +int dvb_fe_get_stats(struct dvb_v5_fe_parms *parms); + +/** + * @brief Retrieve the BER stats from cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param layer DTV layer + * @param scale retrieves the scale + * + * Gets the value for BER stats from stats cache, on a given layer. Layer 0 is + * always present. On DTV standards that doesn't have layers, it returns + * the same value as dvb_fe_retrieve_stats() for layer = 0. + * + * For DTV standards with multiple layers, like ISDB, layer=1 is layer 'A', + * layer=2 is layer 'B' and layer=3 is layer 'C'. Please notice that not all + * frontends support per-layer stats. Also, the layer value is only valid if + * the layer exists at the original stream. + * Also, on such standards, layer 0 is typically a mean value of the layers, + * or a sum of events (if FE_SCALE_COUNTER). + * + * For it to be valid, dvb_fe_get_stats() should be called first. + * + * @return It returns a float number for the BER value. + * If the statistics is not available for any reason, scale will be equal to + * FE_SCALE_NOT_AVAILABLE. + */ +float dvb_fe_retrieve_ber(struct dvb_v5_fe_parms *parms, unsigned layer, + enum fecap_scale_params *scale); + +/** + * @brief Retrieve the PER stats from cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param layer DTV layer + * + * Gets the value for BER stats from stats cache, on a given layer. Layer 0 is + * always present. On DTV standards that doesn't have layers, it returns + * the same value as dvb_fe_retrieve_stats() for layer = 0. + * + * For DTV standards with multiple layers, like ISDB, layer=1 is layer 'A', + * layer=2 is layer 'B' and layer=3 is layer 'C'. Please notice that not all + * frontends support per-layer stats. Also, the layer value is only valid if + * the layer exists at the original stream. + * Also, on such standards, layer 0 is typically a mean value of the layers, + * or a sum of events (if FE_SCALE_COUNTER). + * + * For it to be valid, dvb_fe_get_stats() should be called first. + * + * @return A negative value indicates error. + */ +float dvb_fe_retrieve_per(struct dvb_v5_fe_parms *parms, unsigned layer); + + +/** + * @brief Retrieve the quality stats from cache + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param layer DTV layer + * + * Gets a quality measure for a given layer. Layer 0 is + * always present. On DTV standards that doesn't have layers, it returns + * the same value as dvb_fe_retrieve_stats() for layer = 0. + * + * For DTV standards with multiple layers, like ISDB, layer=1 is layer 'A', + * layer=2 is layer 'B' and layer=3 is layer 'C'. Please notice that not all + * frontends support per-layer stats. Also, the layer value is only valid if + * the layer exists at the original stream. + * Also, on such standards, layer 0 is typically a mean value of the layers, + * or a sum of events (if FE_SCALE_COUNTER). + * + * For it to be valid, dvb_fe_get_stats() should be called first. + * + * @return returns an enum dvb_quantity, where DVB_QUAL_UNKNOWN means that + * the stat isnot available. + */ +enum dvb_quality dvb_fe_retrieve_quality(struct dvb_v5_fe_parms *parms, + unsigned layer); + +/** + * @brief Ancillary function to sprintf on ENG format + * @ingroup frontend + * + * @param buf buffer to store the value + * @param len buffer length + * @param val value to be printed + * + * On ENG notation, the exponential value should be multiple of 3. This is + * good to display some values, like BER. + * + * @return At return, it shows the actual size of the print. A negative value + * indicates an error. + */ +int dvb_fe_snprintf_eng(char *buf, int len, float val); + + +/** + * @brief Ancillary function to sprintf on ENG format + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param cmd DVBv5 or libdvbv5 property + * @param display_name String with the name of the property to be shown + * @param layer DTV Layer + * @param buf buffer to store the value + * @param len buffer length + * @param show_layer_name a value different than zero shows the layer name, if + * the layer is bigger than zero. + * + * This function calls internally dvb_fe_retrieve_stats_layer(). It allows to + * print a DVBv5 statistics value into a string. An extra property is available + * (DTV_QUALITY) with prints either one of the values: Poor, Ok or Good, + * depending on the overall measures. + * + * @return: It returns the length of the printed data. A negative value + * indicates an error. + */ + int dvb_fe_snprintf_stat(struct dvb_v5_fe_parms *parms, uint32_t cmd, + char *display_name, int layer, + char **buf, int *len, int *show_layer_name); + +/** + * @brief Get both status statistics and dvb parameters + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * + * That's similar of calling both dvb_fe_get_parms() and dvb_fe_get_stats(). + * + * @return It returns 0 if success or an errorno otherwise. + */ +int dvb_fe_get_event(struct dvb_v5_fe_parms *parms); + +/* + * Other functions, associated to SEC/LNB/DISEqC + * + * The functions below are just wrappers for the Kernel calls, in order to + * manually control satellite systems. + * + * Instead of using most them, the best is to set the LNBf parameters, and let + * the libdvbv5 to automatically handle the calls. + * + * NOTE: It currently lacks support for two ioctl's: + * FE_DISEQC_RESET_OVERLOAD used only on av7110. + * Spec says: + * If the bus has been automatically powered off due to power overload, + * this ioctl call restores the power to the bus. The call requires read/write + * access to the device. This call has no effect if the device is manually + * powered off. Not all DVB adapters support this ioctl. + * + * FE_DISHNETWORK_SEND_LEGACY_CMD is used on av7110, budget, gp8psk and stv0299 + * Spec says: + * WARNING: This is a very obscure legacy command, used only at stv0299 + * driver. Should not be used on newer drivers. + * It provides a non-standard method for selecting Diseqc voltage on the + * frontend, for Dish Network legacy switches. + * As support for this ioctl were added in 2004, this means that such dishes + * were already legacy in 2004. + * + * So, it doesn't make much sense on implementing support for them. + */ + +/** + * @brief DVB ioctl wrapper for setting SEC voltage + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param on a value different than zero indicates to enable + * voltage on a Satellite Equipment Control (SEC) + * @param v18 if on != 0, a value different than zero means 18 Volts; + * zero means 13 Volts. + * + * If dvb_v5_fe_parms::lnb is set, this is controlled automatically. + */ +int dvb_fe_sec_voltage(struct dvb_v5_fe_parms *parms, int on, int v18); + +/** + * @brief DVB ioctl wrapper for setting SEC tone + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param tone tone setting, as defined by DVB fe_sec_tone_mode_t type + * + * If dvb_v5_fe_parms::lnb is set, this is controlled automatically. + */ +int dvb_fe_sec_tone(struct dvb_v5_fe_parms *parms, fe_sec_tone_mode_t tone); + +/** + * @brief DVB ioctl wrapper for setting LNBf high voltage + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param on a value different than zero indicates to produce + * lightly higher voltages instead of 13/18V, in order + * to compensate for long cables. + */ +int dvb_fe_lnb_high_voltage(struct dvb_v5_fe_parms *parms, int on); + +/** + * @brief DVB ioctl wrapper for setting SEC DiSeqC tone burst to select between + * satellite A or B + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param mini_b if different than zero, sends a 22 KHz tone burst to + * select satellite B. Otherwise, sends tone to select + * satellite A. + * + * Valid only on certain DISEqC arrangements. + * + * If dvb_v5_fe_parms::lnb is set, this is controlled automatically. + */ +int dvb_fe_diseqc_burst(struct dvb_v5_fe_parms *parms, int mini_b); + +/** + * @brief DVB ioctl wrapper for setting SEC DiSeqC command + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param len size of the DiSEqC command + * @param buf DiSEqC command to be sent + * + * If dvb_v5_fe_parms::lnb is set, this is controlled automatically. + */ +int dvb_fe_diseqc_cmd(struct dvb_v5_fe_parms *parms, const unsigned len, + const unsigned char *buf); + +/** + * @brief DVB ioctl wrapper for getting SEC DiSEqC reply + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param len size of the DiSEqC command + * @param buf DiSEqC command to be sent + * @param timeout maximum time to receive the command, in ms. + * + * If dvb_v5_fe_parms::lnb is set, this is controlled automatically. + */ +int dvb_fe_diseqc_reply(struct dvb_v5_fe_parms *parms, unsigned *len, char *buf, + int timeout); + +/** + * @brief DVB Ancillary routine to check if a given Delivery system is satellite + * @ingroup frontend + * + * @param delivery_system delivery system to be selected + */ +int dvb_fe_is_satellite(uint32_t delivery_system); + +/** + * @brief Set default country variant of delivery systems like ISDB-T + * @ingroup frontend + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param country default country, in ISO 3166-1 two letter code. If + * NULL, default charset is guessed from locale environment + * variables. + * + * @return 0 if success or an errorno otherwise. + * + * "COUNTRY" property in dvb_fe_set_parm() overrides the setting. + */ +int dvb_fe_set_default_country(struct dvb_v5_fe_parms *parms, + const char *country); + +#ifdef __cplusplus +} +#endif + +/* + * Arrays from dvb-v5.h + * + * Those arrays can be used to translate from a DVB property into a name. + * + * No need to directly access them from userspace, as dvb_attr_names() + * already handles them into a more standard way. + */ + +#ifndef _DOXYGEN + +extern const unsigned fe_bandwidth_name[8]; +extern const char *dvb_v5_name[71]; +extern const void *dvb_v5_attr_names[]; +extern const char *delivery_system_name[20]; +extern const char *fe_code_rate_name[14]; +extern const char *fe_modulation_name[15]; +extern const char *fe_transmission_mode_name[10]; +extern const unsigned fe_bandwidth_name[8]; +extern const char *fe_guard_interval_name[12]; +extern const char *fe_hierarchy_name[6]; +extern const char *fe_voltage_name[4]; +extern const char *fe_tone_name[3]; +extern const char *fe_inversion_name[4]; +extern const char *fe_pilot_name[4]; +extern const char *fe_rolloff_name[5]; + +#endif + +#endif diff --git a/include/libdvbv5/dvb-file.h b/include/libdvbv5/dvb-file.h new file mode 100644 index 0000000..b46c646 --- /dev/null +++ b/include/libdvbv5/dvb-file.h @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _DVB_FILE_H +#define _DVB_FILE_H + +#include "dvb-fe.h" + +/** + * @file dvb-file.h + * @ingroup file + * @brief Provides interfaces to deal with DVB channel and program files. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * There are basically two types of files used for DVB: + * - files that describe the physical channels (also called as transponders); + * - files that describe the several programs found on a MPEG-TS (also called + * as zap files). + * + * The libdvbv5 library defines an unified type for both types. Other + * applications generally use different formats. + * + * The purpose of the functions and structures defined herein is to provide + * support to read and write to those different formats. + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/* + * DVB structures used to represent all files opened by the libdvbv5 library. + * + * Those structs represents each individual entry on a file, and the file + * as a whole. + */ + +/** + * @struct dvb_elementary_pid + * @brief associates an elementary stream type with its PID + * @ingroup file + * + * @param type Elementary stream type + * @param pid Elementary stream Program ID + */ +struct dvb_elementary_pid { + uint8_t type; + uint16_t pid; +}; + +/** + * @struct dvb_entry + * @brief Represents one entry on a DTV file + * @ingroup file + * + * @param props A property key/value pair. The keys are the ones + * specified at the DVB API, plus the ones defined + * internally by libdvbv5, at the dvb-v5-std.h + * header file. + * @param next a pointer to the next entry. NULL if this is + * the last one. + * @param service_id Service ID associated with a program inside a + * transponder. Please note that pure "channel" + * files will have this field filled with 0. + * @param video_pid Array with the video program IDs inside a service + * @param audio_pid Array with the audio program IDs inside a service + * @param other_el_pid Array with all non-audio/video program IDs + * inside a service + * @param video_pid_len Size of the video_pid array + * @param audio_pid_len Size of the audio_pid array + * @param other_el_pid_len Size of the other_el_pid array + * @param channel String containing the name of the channel + * @param vchannel String representing the Number of the channel + * @param location String representing the location of the channel + * @param sat_number For satellite streams, this represents the + * number of the satellite dish on a DiSeqC + * arrangement. Should be zero on arrangements + * without DiSeqC. + * @param freq_bpf SCR/Unicable band-pass filter frequency to + * use, in kHz. + * For non SRC/Unicable arrangements, it should + * be zero. + * @param diseqc_wait Extra time to wait for DiSeqC commands to + * complete, in ms. The library will use 15 ms + * as the minimal time, + * plus the time specified on this field. + * @param lnb String with the name of the LNBf to be used for + * satellite tuning. The names should match the + * names provided by dvb_sat_get_lnb() call + * (see dvb-sat.h). + */ +struct dvb_entry { + struct dtv_property props[DTV_MAX_COMMAND]; + unsigned int n_props; + struct dvb_entry *next; + uint16_t service_id; + uint16_t *video_pid, *audio_pid; + struct dvb_elementary_pid *other_el_pid; + unsigned video_pid_len, audio_pid_len, other_el_pid_len; + char *channel; + char *vchannel; + + char *location; + + int sat_number; + unsigned freq_bpf; + unsigned diseqc_wait; + char *lnb; +}; + +/** + * @struct dvb_file + * @brief Describes an entire DVB file opened + * + * @param fname name of the file + * @param n_entries number of the entries read + * @param first_entry entry for the first entry. NULL if the file is empty. + */ +struct dvb_file { + char *fname; + int n_entries; + struct dvb_entry *first_entry; +}; + +/* + * DVB file format tables + * + * The structs below are used to represent oneline formats like the ones + * commonly found on DVB legacy applications. + */ + +/** + * @struct dvb_parse_table + * @brief Describes the fields to parse on a file + * @ingroup file + * + * @param prop Name of the DVBv5 or libdvbv5 property field + * @param table Name of a translation table for string to + * int conversion + * @param size Size of the translation table + * @param mult_factor Multiply factor - Used, for example, to + * multiply the symbol rate read from a DVB-S + * table by 1000. + * @param has_default_value It is different than zero when the property + * can be optional. In this case, the next field + * should be present + * @param default_value Default value for the optional field + */ +struct dvb_parse_table { + unsigned int prop; + const char **table; + unsigned int size; + int mult_factor; + int has_default_value; + int default_value; +}; +/** + * @struct dvb_parse_struct + * @brief Describes the format to parse an specific delivery system + * @ingroup file + * + * @param id String that identifies the delivery system on the + * file to be parsed + * @param delsys Delivery system + * @param table the struct dvb_parse_table used to parse for this + * specific delivery system + * @param size Size of the table + */ +struct dvb_parse_struct { + char *id; + uint32_t delsys; + const struct dvb_parse_table *table; + unsigned int size; +}; + +/** + * @struct dvb_parse_file + * @brief Describes an entire file format + * + * @param has_delsys_id A non-zero value indicates that the id field + * at the formats vector should be used + * @param delimiter Delimiters to split entries on the format + * @param formats A struct dvb_parse_struct vector with the + * per delivery system parsers. This table should + * terminate with an empty entry. + */ +struct dvb_parse_file { + int has_delsys_id; + char *delimiter; + struct dvb_parse_struct formats[]; +}; + +/** + * @enum dvb_file_formats + * @brief Known file formats + * @ingroup file + * + * @details + * Please notice that the channel format defined here has a few optional + * fields that aren't part of the dvb-apps format, for DVB-S2 and for DVB-T2. + * They're there to match the formats found at dtv-scan-tables package up to + * September, 5 2014. + * + * @var FILE_UNKNOWN + * @brief File format is unknown + * @var FILE_ZAP + * @brief File is at the dvb-apps "dvbzap" format + * @var FILE_CHANNEL + * @brief File is at the dvb-apps output format for dvb-zap + * @var FILE_DVBV5 + * @brief File is at libdvbv5 format + * @var FILE_VDR + * @brief File is at DVR format (as supported on version 2.1.6). + * Note: this is only supported as an output format. + */ +enum dvb_file_formats { + FILE_UNKNOWN, + FILE_ZAP, + FILE_CHANNEL, + FILE_DVBV5, + FILE_VDR, +}; + +struct dvb_v5_descriptors; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Deallocates memory associated with a struct dvb_file + * @ingroup file + * + * @param dvb_file dvb_file struct to be deallocated + * + * This function assumes that several functions were dynamically allocated + * by the library file functions. + */ +static inline void dvb_file_free(struct dvb_file *dvb_file) +{ + struct dvb_entry *entry = dvb_file->first_entry, *next; + while (entry) { + next = entry->next; + if (entry->channel) + free(entry->channel); + if (entry->vchannel) + free(entry->vchannel); + if (entry->location) + free(entry->location); + if (entry->video_pid) + free(entry->video_pid); + if (entry->audio_pid) + free(entry->audio_pid); + if (entry->other_el_pid) + free(entry->other_el_pid); + if (entry->lnb) + free(entry->lnb); + free(entry); + entry = next; + } + free(dvb_file); +} + +/* + * File format description structures defined for the several formats that + * the library can read natively. + */ + +/** + * @brief File format definitions for dvb-apps channel format + * @ingroup file + */ +extern const struct dvb_parse_file channel_file_format; + +/** + * @brief File format definitions for dvb-apps zap format + * @ingroup file + */ +extern const struct dvb_parse_file channel_file_zap_format; + +/* + * Prototypes for the several functions defined at dvb-file.c + */ + +/** + * @brief Read a file at libdvbv5 format + * @ingroup file + * + * @param fname file name + * + * @return It returns a pointer to struct dvb_file describing the entries that + * were read from the file. If it fails, NULL is returned. + */ +struct dvb_file *dvb_read_file(const char *fname); + +/** + * @brief Write a file at libdvbv5 format + * @ingroup file + * + * @param fname file name + * @param dvb_file contents of the file to be written + * + * @return It returns zero if success, or a positive error number if it fails. + */ +int dvb_write_file(const char *fname, struct dvb_file *dvb_file); + +/** + * @brief Read a file on any format natively supported by + * the library + * @ingroup file + * + * @param fname file name + * @param delsys Delivery system, as specified by enum fe_delivery_system + * @param format Name of the format to be read + * + * @return It returns a pointer to struct dvb_file describing the entries that + * were read from the file. If it fails, NULL is returned. + */ +struct dvb_file *dvb_read_file_format(const char *fname, + uint32_t delsys, + enum dvb_file_formats format); + +/** + * @brief Write a file on any format natively supported by + * the library + * @ingroup file + * + * @param fname file name + * @param dvb_file contents of the file to be written + * @param delsys Delivery system, as specified by enum fe_delivery_system + * @param format Name of the format to be read + * + * @return It a pointer to struct dvb_file on success, NULL otherwise. + */ +int dvb_write_file_format(const char *fname, + struct dvb_file *dvb_file, + uint32_t delsys, + enum dvb_file_formats format); + + +/** + * @brief Stores a key/value pair on a DVB file entry + * @ingroup file + * + * @param entry entry to be filled + * @param cmd key for the property to be used. It be one of the DVBv5 + * properties, plus the libdvbv5 ones, as defined at dvb-v5-std.h + * @param value value for the property. + * + * This function seeks for a property with the name specified by cmd and + * fills it with value. If the entry doesn't exist, it creates a new key. + * + * @return Returns 0 if success, or, if the entry has already DTV_MAX_COMMAND + * properties, it returns -1. + */ +int dvb_store_entry_prop(struct dvb_entry *entry, + uint32_t cmd, uint32_t value); + +/** + * @brief Retrieves the value associated witha key on a DVB file entry + * @ingroup file + * + * @param entry entry to be used + * @param cmd key for the property to be found. It be one of the DVBv5 + * properties, plus the libdvbv5 ones, as defined at dvb-v5-std.h + * @param value pointer to store the value associated with the property. + * + * This function seeks for a property with the name specified by cmd and + * fills value with its contents. + * + * @return Returns 0 if success, or, -1 if the entry doesn't exist. + */ +int dvb_retrieve_entry_prop(struct dvb_entry *entry, + uint32_t cmd, uint32_t *value); + +/** + * @brief stored a new scanned channel into a dvb_file struct + * @ingroup file + * + * @param dvb_file file struct to be filled + * @param parms struct dvb_v5_fe_parms used by libdvbv5 frontend + * @param dvb_desc struct dvb_desc as described at descriptors.h, filled + * with the descriptors associated with a DVB channel. + * those descriptors can be filled by calling one of the + * scan functions defined at dvb-sat.h. + * @param get_detected if different than zero, uses the frontend parameters + * obtained from the device driver (such as modulation, + * FEC, etc) + * @param get_nit if true, uses the parameters obtained from the MPEG-TS + * NIT table to add newly detected transponders. + * + * This function should be used to store the services found on a scanned + * transponder. Initially, it copies the same parameters used to set the + * frontend, that came from a file where the Service ID and Elementary Stream + * PIDs are unknown. At tuning time, it is common to set the device to tune + * on auto-detection mode (e. g. using QAM/AUTO, for example, to autodetect + * the QAM modulation). The libdvbv5's logic will be to check the detected + * values. So, the modulation might, for example, have changed to QAM/256. + * In such case, if get_detected is 0, it will store QAM/AUTO at the struct. + * If get_detected is different than zero, it will store QAM/256. + * If get_nit is different than zero, and if the MPEG-TS has info about other + * physical channels/transponders, this function will add newer entries to + * dvb_file, for it to seek for new transponders. This is very useful especially + * for DVB-C, where all transponders belong to the same operator. Knowing one + * frequency is generally enough to get all DVB-C transponders. + * + * @return Returns 0 if success, or, -1 if error. + */ +int dvb_store_channel(struct dvb_file **dvb_file, + struct dvb_v5_fe_parms *parms, + struct dvb_v5_descriptors *dvb_desc, + int get_detected, int get_nit); + +/** + * @brief Ancillary function that seeks for a delivery system + * @ingroup file + * + * @param name string containing the name of the Delivery System to seek + * + * If the name is found, this function returns the DVBv5 property that + * corresponds to the string given. The function is case-insensitive, and + * it can check for alternate ways to write the name of a Delivery System. + * Currently, it supports: DVB-C, DVB-H, DVB-S, DVB-S2, DVB-T, DVB-T2, + * ISDB-C, ISDB-S, ISDB-T, ATSC-MH, DVBC/ANNEX_A, DVBC/ANNEX_B, DVBT, DSS, + * DVBS, DVBS2, DVBH, ISDBT, ISDBS, ISDBC, ATSC, ATSCMH, DTMB, CMMB, DAB, + * DVBT2, TURBO, DVBC/ANNEX_C. + * Please notice that this doesn't mean that all those standards are properly + * supported by the library. + * + * @return Returns the Delivery System property number if success, -1 if error. + */ +int dvb_parse_delsys(const char *name); + +/** + * @brief Ancillary function that parses the name of a file format + * @ingroup file + * + * @param name string containing the name of the format + * Current valid names are: ZAP, CHANNEL, VDR and DVBV5. + * The name is case-insensitive. + * + * @return It returns FILE_ZAP, FILE_CHANNEL, FILE_VDR or FILE_DVBV5 + * if the name was translated. FILE_UNKNOWN otherwise. + */ +enum dvb_file_formats dvb_parse_format(const char *name); + +/* + * Routines to read a non-libdvbv5 format. They're called by + * dvb_read_file_format() or dvb_write_file_format() + */ + +/** + * @brief Read and parses a one line file format + * @ingroup file + * + * @param fname file name + * @param delsys delivery system + * @param parse_file pointer struct dvb_parse_file + * + * @return It a pointer to struct dvb_file on success, NULL otherwise. + * + * This function is called internally by dvb_read_file_format. + */ +struct dvb_file *dvb_parse_format_oneline(const char *fname, + uint32_t delsys, + const struct dvb_parse_file *parse_file); + +/** + * @brief Writes a file into an one line file format + * @ingroup file + * + * @param fname file name + * @param dvb_file contents of the file to be written + * @param delsys delivery system + * @param parse_file pointer struct dvb_parse_file + * + * @return It returns zero if success, or a positive error number if it fails. + * + * This function is called internally by dvb_write_file_format. + */ +int dvb_write_format_oneline(const char *fname, + struct dvb_file *dvb_file, + uint32_t delsys, + const struct dvb_parse_file *parse_file); + +/** + * @brief Writes a file into vdr format (compatible up to version 2.1) + * @ingroup file + * + * @param fname file name + * @param dvb_file contents of the file to be written + * + * @return It returns zero if success, or a positive error number if it fails. + * + * This function is called internally by dvb_write_file_format. + */ +int dvb_write_format_vdr(const char *fname, + struct dvb_file *dvb_file); + +#ifdef __cplusplus +} +#endif + +#endif // _DVB_FILE_H diff --git a/include/libdvbv5/dvb-frontend.h b/include/libdvbv5/dvb-frontend.h new file mode 100644 index 0000000..9c11eab --- /dev/null +++ b/include/libdvbv5/dvb-frontend.h @@ -0,0 +1,592 @@ +/* + * frontend.h + * + * Copyright (C) 2000 Marcus Metzler + * Ralph Metzler + * Holger Waechtler + * Andre Draszik + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License, + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DVBFRONTEND_H_ +#define _DVBFRONTEND_H_ + +#include + +typedef enum fe_type { + FE_QPSK, + FE_QAM, + FE_OFDM, + FE_ATSC +} fe_type_t; + + +typedef enum fe_caps { + FE_IS_STUPID = 0, + FE_CAN_INVERSION_AUTO = 0x1, + FE_CAN_FEC_1_2 = 0x2, + FE_CAN_FEC_2_3 = 0x4, + FE_CAN_FEC_3_4 = 0x8, + FE_CAN_FEC_4_5 = 0x10, + FE_CAN_FEC_5_6 = 0x20, + FE_CAN_FEC_6_7 = 0x40, + FE_CAN_FEC_7_8 = 0x80, + FE_CAN_FEC_8_9 = 0x100, + FE_CAN_FEC_AUTO = 0x200, + FE_CAN_QPSK = 0x400, + FE_CAN_QAM_16 = 0x800, + FE_CAN_QAM_32 = 0x1000, + FE_CAN_QAM_64 = 0x2000, + FE_CAN_QAM_128 = 0x4000, + FE_CAN_QAM_256 = 0x8000, + FE_CAN_QAM_AUTO = 0x10000, + FE_CAN_TRANSMISSION_MODE_AUTO = 0x20000, + FE_CAN_BANDWIDTH_AUTO = 0x40000, + FE_CAN_GUARD_INTERVAL_AUTO = 0x80000, + FE_CAN_HIERARCHY_AUTO = 0x100000, + FE_CAN_8VSB = 0x200000, + FE_CAN_16VSB = 0x400000, + FE_HAS_EXTENDED_CAPS = 0x800000, /* We need more bitspace for newer APIs, indicate this. */ + FE_CAN_MULTISTREAM = 0x4000000, /* frontend supports multistream filtering */ + FE_CAN_TURBO_FEC = 0x8000000, /* frontend supports "turbo fec modulation" */ + FE_CAN_2G_MODULATION = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */ + FE_NEEDS_BENDING = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */ + FE_CAN_RECOVER = 0x40000000, /* frontend can recover from a cable unplug automatically */ + FE_CAN_MUTE_TS = 0x80000000 /* frontend can stop spurious TS data output */ +} fe_caps_t; + + +struct dvb_frontend_info { + char name[128]; + fe_type_t type; /* DEPRECATED. Use DTV_ENUM_DELSYS instead */ + __u32 frequency_min; + __u32 frequency_max; + __u32 frequency_stepsize; + __u32 frequency_tolerance; + __u32 symbol_rate_min; + __u32 symbol_rate_max; + __u32 symbol_rate_tolerance; /* ppm */ + __u32 notifier_delay; /* DEPRECATED */ + fe_caps_t caps; +}; + + +/** + * Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for + * the meaning of this struct... + */ +struct dvb_diseqc_master_cmd { + __u8 msg [6]; /* { framing, address, command, data [3] } */ + __u8 msg_len; /* valid values are 3...6 */ +}; + + +struct dvb_diseqc_slave_reply { + __u8 msg [4]; /* { framing, data [3] } */ + __u8 msg_len; /* valid values are 0...4, 0 means no msg */ + int timeout; /* return from ioctl after timeout ms with */ +}; /* errorcode when no message was received */ + + +typedef enum fe_sec_voltage { + SEC_VOLTAGE_13, + SEC_VOLTAGE_18, + SEC_VOLTAGE_OFF +} fe_sec_voltage_t; + + +typedef enum fe_sec_tone_mode { + SEC_TONE_ON, + SEC_TONE_OFF +} fe_sec_tone_mode_t; + + +typedef enum fe_sec_mini_cmd { + SEC_MINI_A, + SEC_MINI_B +} fe_sec_mini_cmd_t; + + +/** + * enum fe_status - enumerates the possible frontend status + * @FE_HAS_SIGNAL: found something above the noise level + * @FE_HAS_CARRIER: found a DVB signal + * @FE_HAS_VITERBI: FEC is stable + * @FE_HAS_SYNC: found sync bytes + * @FE_HAS_LOCK: everything's working + * @FE_TIMEDOUT: no lock within the last ~2 seconds + * @FE_REINIT: frontend was reinitialized, application is recommended + * to reset DiSEqC, tone and parameters + */ + +typedef enum fe_status { + FE_HAS_SIGNAL = 0x01, + FE_HAS_CARRIER = 0x02, + FE_HAS_VITERBI = 0x04, + FE_HAS_SYNC = 0x08, + FE_HAS_LOCK = 0x10, + FE_TIMEDOUT = 0x20, + FE_REINIT = 0x40, +} fe_status_t; + +typedef enum fe_spectral_inversion { + INVERSION_OFF, + INVERSION_ON, + INVERSION_AUTO +} fe_spectral_inversion_t; + + +typedef enum fe_code_rate { + FEC_NONE = 0, + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_4_5, + FEC_5_6, + FEC_6_7, + FEC_7_8, + FEC_8_9, + FEC_AUTO, + FEC_3_5, + FEC_9_10, + FEC_2_5, +} fe_code_rate_t; + + +typedef enum fe_modulation { + QPSK, + QAM_16, + QAM_32, + QAM_64, + QAM_128, + QAM_256, + QAM_AUTO, + VSB_8, + VSB_16, + PSK_8, + APSK_16, + APSK_32, + DQPSK, + QAM_4_NR, +} fe_modulation_t; + +typedef enum fe_transmit_mode { + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_8K, + TRANSMISSION_MODE_AUTO, + TRANSMISSION_MODE_4K, + TRANSMISSION_MODE_1K, + TRANSMISSION_MODE_16K, + TRANSMISSION_MODE_32K, + TRANSMISSION_MODE_C1, + TRANSMISSION_MODE_C3780, +} fe_transmit_mode_t; + +#if defined(__DVB_CORE__) || !defined (__KERNEL__) +typedef enum fe_bandwidth { + BANDWIDTH_8_MHZ, + BANDWIDTH_7_MHZ, + BANDWIDTH_6_MHZ, + BANDWIDTH_AUTO, + BANDWIDTH_5_MHZ, + BANDWIDTH_10_MHZ, + BANDWIDTH_1_712_MHZ, +} fe_bandwidth_t; +#endif + +typedef enum fe_guard_interval { + GUARD_INTERVAL_1_32, + GUARD_INTERVAL_1_16, + GUARD_INTERVAL_1_8, + GUARD_INTERVAL_1_4, + GUARD_INTERVAL_AUTO, + GUARD_INTERVAL_1_128, + GUARD_INTERVAL_19_128, + GUARD_INTERVAL_19_256, + GUARD_INTERVAL_PN420, + GUARD_INTERVAL_PN595, + GUARD_INTERVAL_PN945, +} fe_guard_interval_t; + + +typedef enum fe_hierarchy { + HIERARCHY_NONE, + HIERARCHY_1, + HIERARCHY_2, + HIERARCHY_4, + HIERARCHY_AUTO +} fe_hierarchy_t; + +enum fe_interleaving { + INTERLEAVING_NONE, + INTERLEAVING_AUTO, + INTERLEAVING_240, + INTERLEAVING_720, +}; + +#if defined(__DVB_CORE__) || !defined (__KERNEL__) +struct dvb_qpsk_parameters { + __u32 symbol_rate; /* symbol rate in Symbols per second */ + fe_code_rate_t fec_inner; /* forward error correction (see above) */ +}; + +struct dvb_qam_parameters { + __u32 symbol_rate; /* symbol rate in Symbols per second */ + fe_code_rate_t fec_inner; /* forward error correction (see above) */ + fe_modulation_t modulation; /* modulation type (see above) */ +}; + +struct dvb_vsb_parameters { + fe_modulation_t modulation; /* modulation type (see above) */ +}; + +struct dvb_ofdm_parameters { + fe_bandwidth_t bandwidth; + fe_code_rate_t code_rate_HP; /* high priority stream code rate */ + fe_code_rate_t code_rate_LP; /* low priority stream code rate */ + fe_modulation_t constellation; /* modulation type (see above) */ + fe_transmit_mode_t transmission_mode; + fe_guard_interval_t guard_interval; + fe_hierarchy_t hierarchy_information; +}; + + +struct dvb_frontend_parameters { + __u32 frequency; /* (absolute) frequency in Hz for QAM/OFDM/ATSC */ + /* intermediate frequency in kHz for QPSK */ + fe_spectral_inversion_t inversion; + union { + struct dvb_qpsk_parameters qpsk; + struct dvb_qam_parameters qam; + struct dvb_ofdm_parameters ofdm; + struct dvb_vsb_parameters vsb; + } u; +}; + +struct dvb_frontend_event { + fe_status_t status; + struct dvb_frontend_parameters parameters; +}; +#endif + +/* S2API Commands */ +#define DTV_UNDEFINED 0 +#define DTV_TUNE 1 +#define DTV_CLEAR 2 +#define DTV_FREQUENCY 3 +#define DTV_MODULATION 4 +#define DTV_BANDWIDTH_HZ 5 +#define DTV_INVERSION 6 +#define DTV_DISEQC_MASTER 7 +#define DTV_SYMBOL_RATE 8 +#define DTV_INNER_FEC 9 +#define DTV_VOLTAGE 10 +#define DTV_TONE 11 +#define DTV_PILOT 12 +#define DTV_ROLLOFF 13 +#define DTV_DISEQC_SLAVE_REPLY 14 + +/* Basic enumeration set for querying unlimited capabilities */ +#define DTV_FE_CAPABILITY_COUNT 15 +#define DTV_FE_CAPABILITY 16 +#define DTV_DELIVERY_SYSTEM 17 + +/* ISDB-T and ISDB-Tsb */ +#define DTV_ISDBT_PARTIAL_RECEPTION 18 +#define DTV_ISDBT_SOUND_BROADCASTING 19 + +#define DTV_ISDBT_SB_SUBCHANNEL_ID 20 +#define DTV_ISDBT_SB_SEGMENT_IDX 21 +#define DTV_ISDBT_SB_SEGMENT_COUNT 22 + +#define DTV_ISDBT_LAYERA_FEC 23 +#define DTV_ISDBT_LAYERA_MODULATION 24 +#define DTV_ISDBT_LAYERA_SEGMENT_COUNT 25 +#define DTV_ISDBT_LAYERA_TIME_INTERLEAVING 26 + +#define DTV_ISDBT_LAYERB_FEC 27 +#define DTV_ISDBT_LAYERB_MODULATION 28 +#define DTV_ISDBT_LAYERB_SEGMENT_COUNT 29 +#define DTV_ISDBT_LAYERB_TIME_INTERLEAVING 30 + +#define DTV_ISDBT_LAYERC_FEC 31 +#define DTV_ISDBT_LAYERC_MODULATION 32 +#define DTV_ISDBT_LAYERC_SEGMENT_COUNT 33 +#define DTV_ISDBT_LAYERC_TIME_INTERLEAVING 34 + +#define DTV_API_VERSION 35 + +#define DTV_CODE_RATE_HP 36 +#define DTV_CODE_RATE_LP 37 +#define DTV_GUARD_INTERVAL 38 +#define DTV_TRANSMISSION_MODE 39 +#define DTV_HIERARCHY 40 + +#define DTV_ISDBT_LAYER_ENABLED 41 + +#define DTV_STREAM_ID 42 +#define DTV_ISDBS_TS_ID_LEGACY DTV_STREAM_ID +#define DTV_DVBT2_PLP_ID_LEGACY 43 + +#define DTV_ENUM_DELSYS 44 + +/* ATSC-MH */ +#define DTV_ATSCMH_FIC_VER 45 +#define DTV_ATSCMH_PARADE_ID 46 +#define DTV_ATSCMH_NOG 47 +#define DTV_ATSCMH_TNOG 48 +#define DTV_ATSCMH_SGN 49 +#define DTV_ATSCMH_PRC 50 +#define DTV_ATSCMH_RS_FRAME_MODE 51 +#define DTV_ATSCMH_RS_FRAME_ENSEMBLE 52 +#define DTV_ATSCMH_RS_CODE_MODE_PRI 53 +#define DTV_ATSCMH_RS_CODE_MODE_SEC 54 +#define DTV_ATSCMH_SCCC_BLOCK_MODE 55 +#define DTV_ATSCMH_SCCC_CODE_MODE_A 56 +#define DTV_ATSCMH_SCCC_CODE_MODE_B 57 +#define DTV_ATSCMH_SCCC_CODE_MODE_C 58 +#define DTV_ATSCMH_SCCC_CODE_MODE_D 59 + +#define DTV_INTERLEAVING 60 +#define DTV_LNA 61 + +/* Quality parameters */ +#define DTV_STAT_SIGNAL_STRENGTH 62 +#define DTV_STAT_CNR 63 +#define DTV_STAT_PRE_ERROR_BIT_COUNT 64 +#define DTV_STAT_PRE_TOTAL_BIT_COUNT 65 +#define DTV_STAT_POST_ERROR_BIT_COUNT 66 +#define DTV_STAT_POST_TOTAL_BIT_COUNT 67 +#define DTV_STAT_ERROR_BLOCK_COUNT 68 +#define DTV_STAT_TOTAL_BLOCK_COUNT 69 + +#define DTV_MAX_COMMAND DTV_STAT_TOTAL_BLOCK_COUNT + +typedef enum fe_pilot { + PILOT_ON, + PILOT_OFF, + PILOT_AUTO, +} fe_pilot_t; + +typedef enum fe_rolloff { + ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */ + ROLLOFF_20, + ROLLOFF_25, + ROLLOFF_AUTO, +} fe_rolloff_t; + +typedef enum fe_delivery_system { + SYS_UNDEFINED, + SYS_DVBC_ANNEX_A, + SYS_DVBC_ANNEX_B, + SYS_DVBT, + SYS_DSS, + SYS_DVBS, + SYS_DVBS2, + SYS_DVBH, + SYS_ISDBT, + SYS_ISDBS, + SYS_ISDBC, + SYS_ATSC, + SYS_ATSCMH, + SYS_DTMB, + SYS_CMMB, + SYS_DAB, + SYS_DVBT2, + SYS_TURBO, + SYS_DVBC_ANNEX_C, +} fe_delivery_system_t; + +/* backward compatibility */ +#define SYS_DVBC_ANNEX_AC SYS_DVBC_ANNEX_A +#define SYS_DMBTH SYS_DTMB /* DMB-TH is legacy name, use DTMB instead */ + +/* ATSC-MH */ + +enum atscmh_sccc_block_mode { + ATSCMH_SCCC_BLK_SEP = 0, + ATSCMH_SCCC_BLK_COMB = 1, + ATSCMH_SCCC_BLK_RES = 2, +}; + +enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, + ATSCMH_SCCC_CODE_RES = 2, +}; + +enum atscmh_rs_frame_ensemble { + ATSCMH_RSFRAME_ENS_PRI = 0, + ATSCMH_RSFRAME_ENS_SEC = 1, +}; + +enum atscmh_rs_frame_mode { + ATSCMH_RSFRAME_PRI_ONLY = 0, + ATSCMH_RSFRAME_PRI_SEC = 1, + ATSCMH_RSFRAME_RES = 2, +}; + +enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, + ATSCMH_RSCODE_RES = 3, +}; + +#define NO_STREAM_ID_FILTER (~0U) +#define LNA_AUTO (~0U) + +struct dtv_cmds_h { + char *name; /* A display name for debugging purposes */ + + __u32 cmd; /* A unique ID */ + + /* Flags */ + __u32 set:1; /* Either a set or get property */ + __u32 buffer:1; /* Does this property use the buffer? */ + __u32 reserved:30; /* Align */ +}; + +/** + * Scale types for the quality parameters. + * @FE_SCALE_NOT_AVAILABLE: That QoS measure is not available. That + * could indicate a temporary or a permanent + * condition. + * @FE_SCALE_DECIBEL: The scale is measured in 0.0001 dB steps, typically + * used on signal measures. + * @FE_SCALE_RELATIVE: The scale is a relative percentual measure, + * ranging from 0 (0%) to 0xffff (100%). + * @FE_SCALE_COUNTER: The scale counts the occurrence of an event, like + * bit error, block error, lapsed time. + */ +enum fecap_scale_params { + FE_SCALE_NOT_AVAILABLE = 0, + FE_SCALE_DECIBEL, + FE_SCALE_RELATIVE, + FE_SCALE_COUNTER +}; + +/** + * struct dtv_stats - Used for reading a DTV status property + * + * @value: value of the measure. Should range from 0 to 0xffff; + * @scale: Filled with enum fecap_scale_params - the scale + * in usage for that parameter + * + * For most delivery systems, this will return a single value for each + * parameter. + * It should be noticed, however, that new OFDM delivery systems like + * ISDB can use different modulation types for each group of carriers. + * On such standards, up to 8 groups of statistics can be provided, one + * for each carrier group (called "layer" on ISDB). + * In order to be consistent with other delivery systems, the first + * value refers to the entire set of carriers ("global"). + * dtv_status:scale should use the value FE_SCALE_NOT_AVAILABLE when + * the value for the entire group of carriers or from one specific layer + * is not provided by the hardware. + * st.len should be filled with the latest filled status + 1. + * + * In other words, for ISDB, those values should be filled like: + * u.st.stat.svalue[0] = global statistics; + * u.st.stat.scale[0] = FE_SCALE_DECIBELS; + * u.st.stat.value[1] = layer A statistics; + * u.st.stat.scale[1] = FE_SCALE_NOT_AVAILABLE (if not available); + * u.st.stat.svalue[2] = layer B statistics; + * u.st.stat.scale[2] = FE_SCALE_DECIBELS; + * u.st.stat.svalue[3] = layer C statistics; + * u.st.stat.scale[3] = FE_SCALE_DECIBELS; + * u.st.len = 4; + */ +struct dtv_stats { + __u8 scale; /* enum fecap_scale_params type */ + union { + __u64 uvalue; /* for counters and relative scales */ + __s64 svalue; /* for 0.0001 dB measures */ + }; +} __attribute__ ((packed)); + + +#define MAX_DTV_STATS 4 + +struct dtv_fe_stats { + __u8 len; + struct dtv_stats stat[MAX_DTV_STATS]; +} __attribute__ ((packed)); + +struct dtv_property { + __u32 cmd; + __u32 reserved[3]; + union { + __u32 data; + struct dtv_fe_stats st; + struct { + __u8 data[32]; + __u32 len; + __u32 reserved1[3]; + void *reserved2; + } buffer; + } u; + int result; +} __attribute__ ((packed)); + +/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */ +#define DTV_IOCTL_MAX_MSGS 64 + +struct dtv_properties { + __u32 num; + struct dtv_property *props; +}; + +#define FE_SET_PROPERTY _IOW('o', 82, struct dtv_properties) +#define FE_GET_PROPERTY _IOR('o', 83, struct dtv_properties) + + +/** + * When set, this flag will disable any zigzagging or other "normal" tuning + * behaviour. Additionally, there will be no automatic monitoring of the lock + * status, and hence no frontend events will be generated. If a frontend device + * is closed, this flag will be automatically turned off when the device is + * reopened read-write. + */ +#define FE_TUNE_MODE_ONESHOT 0x01 + + +#define FE_GET_INFO _IOR('o', 61, struct dvb_frontend_info) + +#define FE_DISEQC_RESET_OVERLOAD _IO('o', 62) +#define FE_DISEQC_SEND_MASTER_CMD _IOW('o', 63, struct dvb_diseqc_master_cmd) +#define FE_DISEQC_RECV_SLAVE_REPLY _IOR('o', 64, struct dvb_diseqc_slave_reply) +#define FE_DISEQC_SEND_BURST _IO('o', 65) /* fe_sec_mini_cmd_t */ + +#define FE_SET_TONE _IO('o', 66) /* fe_sec_tone_mode_t */ +#define FE_SET_VOLTAGE _IO('o', 67) /* fe_sec_voltage_t */ +#define FE_ENABLE_HIGH_LNB_VOLTAGE _IO('o', 68) /* int */ + +#define FE_READ_STATUS _IOR('o', 69, fe_status_t) +#define FE_READ_BER _IOR('o', 70, __u32) +#define FE_READ_SIGNAL_STRENGTH _IOR('o', 71, __u16) +#define FE_READ_SNR _IOR('o', 72, __u16) +#define FE_READ_UNCORRECTED_BLOCKS _IOR('o', 73, __u32) + +#define FE_SET_FRONTEND _IOW('o', 76, struct dvb_frontend_parameters) +#define FE_GET_FRONTEND _IOR('o', 77, struct dvb_frontend_parameters) +#define FE_SET_FRONTEND_TUNE_MODE _IO('o', 81) /* unsigned int */ +#define FE_GET_EVENT _IOR('o', 78, struct dvb_frontend_event) + +#define FE_DISHNETWORK_SEND_LEGACY_CMD _IO('o', 80) /* unsigned int */ + +#endif /*_DVBFRONTEND_H_*/ diff --git a/include/libdvbv5/dvb-log.h b/include/libdvbv5/dvb-log.h new file mode 100644 index 0000000..d07870f --- /dev/null +++ b/include/libdvbv5/dvb-log.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _LOG_H +#define _LOG_H + +#include + +/** + * @file dvb-log.h + * @ingroup ancillary + * @brief Provides interfaces to handle libdvbv5 log messages. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/** + * @typedef void (*dvb_logfunc)(int level, const char *fmt, ...) + * @brief typedef used by dvb_fe_open2 for the log function + * @ingroup ancillary + */ + +typedef void (*dvb_logfunc)(int level, const char *fmt, ...) __attribute__ (( format( printf, 2, 3 ))); + +/* + * Macros used internally inside libdvbv5 frontend part, to output logs + */ + +#ifndef _DOXYGEN + +#ifndef __DVB_FE_PRIV_H + +#define dvb_log(fmt, arg...) do {\ + parms->logfunc(LOG_INFO, fmt, ##arg); \ +} while (0) +#define dvb_logerr(fmt, arg...) do {\ + parms->logfunc(LOG_ERR, fmt, ##arg); \ +} while (0) +#define dvb_logdbg(fmt, arg...) do {\ + parms->logfunc(LOG_DEBUG, fmt, ##arg); \ +} while (0) +#define dvb_logwarn(fmt, arg...) do {\ + parms->logfunc(LOG_WARNING, fmt, ##arg); \ +} while (0) +#define dvb_loginfo(fmt, arg...) do {\ + parms->logfunc(LOG_NOTICE, fmt, ##arg); \ +} while (0) + +#define dvb_perror(msg) do {\ + parms->logfunc(LOG_ERR, "%s: %s", msg, strerror(errno)); \ +} while (0) + +#else + +#define dvb_log(fmt, arg...) do {\ + parms->p.logfunc(LOG_INFO, fmt, ##arg); \ +} while (0) +#define dvb_logerr(fmt, arg...) do {\ + parms->p.logfunc(LOG_ERR, fmt, ##arg); \ +} while (0) +#define dvb_logdbg(fmt, arg...) do {\ + parms->p.logfunc(LOG_DEBUG, fmt, ##arg); \ +} while (0) +#define dvb_logwarn(fmt, arg...) do {\ + parms->p.logfunc(LOG_WARNING, fmt, ##arg); \ +} while (0) +#define dvb_loginfo(fmt, arg...) do {\ + parms->p.logfunc(LOG_NOTICE, fmt, ##arg); \ +} while (0) + +#define dvb_perror(msg) do {\ + parms->p.logfunc(LOG_ERR, "%s: %s", msg, strerror(errno)); \ +} while (0) + +#endif + +#endif /* _DOXYGEN */ + +/** + * @brief This is the prototype of the internal log function that it is used, + * if the library client doesn't desire to override with something else. + * @ingroup ancillary + * + * @param level level of the message, as defined at syslog.h + * @param fmt format string (same as format string on sprintf) + */ +void dvb_default_log(int level, const char *fmt, ...) __attribute__ (( format( printf, 2, 3 ))); + +#endif diff --git a/include/libdvbv5/dvb-sat.h b/include/libdvbv5/dvb-sat.h new file mode 100644 index 0000000..c80545c --- /dev/null +++ b/include/libdvbv5/dvb-sat.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ +#ifndef _LIBSAT_H +#define _LIBSAT_H + +#include "dvb-v5-std.h" + +/** + * @file dvb-sat.h + * @ingroup satellite + * @brief Provides interfaces to deal with DVB Satellite systems. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/* + * Satellite handling functions + */ + +/** + * @struct dvbsat_freqrange + * @brief Defines a frequency range used by Satellite + * @ingroup satellite + * + * @param low low frequency, in kHz + * @param high high frequency, in kHz + */ +struct dvbsat_freqrange { + unsigned low, high; +}; + +/** + * @struct dvb_sat_lnb + * @brief Stores the information of a LNBf + * @ingroup satellite + * + * @param name long name of the LNBf type + * @param alias short name for the LNBf type + * @param lowfreq Low frequency Intermediate Frequency of the LNBf, in kHz + * @param highfreq High frequency Intermediate frequency of the LNBf, + * in kHz + * @param rangeswitch For LNBf that has multiple frequency ranges controlled + * by a voltage change, specify the start frequency where + * the second range will be applied. + * @param freqrange Contains the range(s) of frequencies supported by a + * given LNBf. + * + * The LNBf (low-noise block downconverter) is a type of amplifier that is + * installed inside the parabolic dishes. It converts the antenna signal to + * an Intermediate Frequency. Several Ku-band LNBf have more than one IF. + * The lower IF is stored at lowfreq, the higher IF at highfreq. + * The exact setup for those structs actually depend on the model of the LNBf, + * and its usage. + */ +struct dvb_sat_lnb { + const char *name; + const char *alias; + unsigned lowfreq, highfreq; + + unsigned rangeswitch; + + struct dvbsat_freqrange freqrange[2]; +}; + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/* From libsat.c */ + +/** + * @brief search for a LNBf entry + * @ingroup satellite + * + * @param name name of the LNBf entry to seek. + * + * On sucess, it returns a non-negative number with corresponds to the LNBf + * entry inside the LNBf structure at dvb-sat.c. + * + * @return A -1 return code indicates that the LNBf was not found. + */ +int dvb_sat_search_lnb(const char *name); + +/** + * @brief prints the contents of a LNBf entry at STDOUT. + * @ingroup satellite + * + * @param index index for the entry + * + * @return returns -1 if the index is out of range, zero otherwise. + */ +int dvb_print_lnb(int index); + +/** + * @brief Prints all LNBf entries at STDOUT. + * @ingroup satellite + * + * This function doesn't return anything. Internally, it calls dvb_print_lnb() + * for all entries inside its LNBf database. + */ +void dvb_print_all_lnb(void); + +/** + * @brief gets a LNBf entry at its internal database + * @ingroup satellite + * + * @param index index for the entry. + * + * @return returns NULL if not found, of a struct dvb_sat_lnb pointer otherwise. + */ +const struct dvb_sat_lnb *dvb_sat_get_lnb(int index); + +/** + * @brief sets the satellite parameters + * @ingroup satellite + * + * @param parms struct dvb_v5_fe_parms pointer. + * + * This function is called internally by the library to set the LNBf + * parameters, if the dvb_v5_fe_parms::lnb field is filled. + * + * @return 0 on success. + */ +int dvb_sat_set_parms(struct dvb_v5_fe_parms *parms); + +#ifdef __cplusplus +} +#endif + +#endif // _LIBSAT_H diff --git a/include/libdvbv5/dvb-scan.h b/include/libdvbv5/dvb-scan.h new file mode 100644 index 0000000..c480426 --- /dev/null +++ b/include/libdvbv5/dvb-scan.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ +#ifndef _LIBSCAN_H +#define _LIBSCAN_H + +#include +#include +#include +#include + +/** + * @file dvb-scan.h + * @ingroup frontend_scan + * @brief Provides interfaces to scan programs inside MPEG-TS digital TV streams. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/* According with ISO/IEC 13818-1:2007 */ + +#define MAX_TABLE_SIZE 1024 * 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +struct dvb_entry; + +/** + * @struct dvb_v5_descriptors_program + * @brief Associates PMT with PAT tables + * @ingroup frontend_scan + * + * @param pat_pgm pointer for PAT descriptor + * @param pmt pointer for PMT descriptor + */ +struct dvb_v5_descriptors_program { + struct dvb_table_pat_program *pat_pgm; + struct dvb_table_pmt *pmt; +}; + +/** + * @struct dvb_v5_descriptors + * @brief Contains the descriptors needed to scan the Service ID and other relevant info at a MPEG-TS Digital TV stream + * @ingroup frontend_scan + * + * @param delivery_system Delivery system of the parsed MPEG-TS + * @param entry struct dvb_entry pointer (see dvb-file.h) + * @param pat PAT table descriptor pointer + * @param vct VCT table descriptor pointer + * @param program PAT/PMT array associated programs found at MPEG-TS + * @param nit NIT table descriptor pointer + * @param sdt SDT table descriptor pointer + * @param num_program Number of program entries at program array. + * + * Those descriptors are filled by the scan routines when the tables are + * found. Otherwise, they're NULL. + */ +struct dvb_v5_descriptors { + uint32_t delivery_system; + + struct dvb_entry *entry; + unsigned num_entry; + + struct dvb_table_pat *pat; + struct atsc_table_vct *vct; + struct dvb_v5_descriptors_program *program; + struct dvb_table_nit *nit; + struct dvb_table_sdt *sdt; + + unsigned num_program; +}; + +/** + * @struct dvb_table_filter + * @brief Describes the PES filters used by DVB scan + * @ingroup frontend_scan + * + * @param tid Table ID + * @param pid Program ID + * @param ts_id Table section ID (for multisession filtering). If no + * specific table section is needed, -1 should be used + * @param table pointer to a pointer for the table struct to be filled + * @param allow_section_gaps Allow non-continuous section numbering + * @param priv Internal structure used inside the DVB core. shouldn't + * be touched externally. + */ +struct dvb_table_filter { + /* Input data */ + unsigned char tid; + uint16_t pid; + int ts_id; + void **table; + + int allow_section_gaps; + + /* + * Private temp data used by dvb_read_sections(). + * Should not be filled outside dvb-scan.c, as they'll be + * overrided + */ + void *priv; +}; +/** + * @brief deallocates all data associated with a table filter + * @ingroup frontend_scan + * + * @param sect table filter pointer + */ +void dvb_table_filter_free(struct dvb_table_filter *sect); + +/** + * @brief read MPEG-TS tables that comes from a DTV card + * @ingroup frontend_scan + * + * @param parms pointer to struct dvb_v5_fe_parms created when the + * frontend is opened + * @param dmx_fd an opened demux file descriptor + * @param tid Table ID + * @param pid Program ID + * @param table pointer to a pointer for the table struct to be filled + * @param timeout Limit, in seconds, to read a MPEG-TS table + * + * This function is used to read the DVB tables by specifying a table ID and + * a program ID. The libdvbv5 should have a parser for the descriptors of the + * table type that should be parsed. + * The table will be automatically allocated on success. + * The function will read on the specified demux and return when reading is + * done or an error has occurred. If table is not NULL after the call, it has + * to be freed with the apropriate free table function (even if an error has + * occurred). + * + * If the application wants to abort the read operation, it can change the + * value of parms->p.abort to 1. + * + * Returns 0 on success or a negative error code. + * + * Example usage: + * @code + * struct dvb_table_pat *pat; + * int r = dvb_read_section( parms, dmx_fd, DVB_TABLE_PAT, DVB_TABLE_PAT_PID, + * (void **) &pat, 5 ); + * if (r < 0) + * dvb_logerr("error reading PAT table"); + * else { + * // do something with pat + * } + * if (pat) + * dvb_table_pat_free( pat ); + * @endcode + */ +int dvb_read_section(struct dvb_v5_fe_parms *parms, int dmx_fd, + unsigned char tid, uint16_t pid, void **table, + unsigned timeout); + +/** + * @brief read MPEG-TS tables that comes from a DTV card + * with an specific table section ID + * @ingroup frontend_scan + * + * @param parms pointer to struct dvb_v5_fe_parms created when the + * frontend is opened + * @param dmx_fd an opened demux file descriptor + * @param tid Table ID + * @param pid Program ID + * @param ts_id Table section ID (for multisession filtering). If no + * specific table section is needed, -1 should be used + * @param table pointer to a pointer for the table struct to be filled + * @param timeout limit, in seconds, to read a MPEG-TS table + * + * This is a variant of dvb_read_section() that also seeks for an specific + * table section ID given by ts_id. + */ + int dvb_read_section_with_id(struct dvb_v5_fe_parms *parms, int dmx_fd, + unsigned char tid, uint16_t pid, int ts_id, + void **table, unsigned timeout); + +/** + * @brief read MPEG-TS tables that comes from a DTV card + * @ingroup frontend_scan + * + * @param parms pointer to struct dvb_v5_fe_parms created when the + * frontend is opened + * @param dmx_fd an opened demux file descriptor + * @param sect section filter pointer + * @param timeout limit, in seconds, to read a MPEG-TS table + * + * This is a variant of dvb_read_section() that uses a struct dvb_table_filter + * to specify the filter to use. + */ +int dvb_read_sections(struct dvb_v5_fe_parms *parms, int dmx_fd, + struct dvb_table_filter *sect, + unsigned timeout); + +/** + * @brief allocates a struct dvb_v5_descriptors + * @ingroup frontend_scan + * + * @param delivery_system Delivery system to be used on the table + * + * At success, returns a pointer. NULL otherwise. + */ +struct dvb_v5_descriptors *dvb_scan_alloc_handler_table(uint32_t delivery_system); + +/** + * @brief frees a struct dvb_v5_descriptors + * @ingroup frontend_scan + * + * @param dvb_scan_handler pointer to the struct to be freed. + */ +void dvb_scan_free_handler_table(struct dvb_v5_descriptors *dvb_scan_handler); + +/** + * @brief Scans a DVB stream, looking for the tables needed to + * identify the programs inside a MPEG-TS + * @ingroup frontend_scan + * + * @param parms pointer to struct dvb_v5_fe_parms created when + * the frontend is opened + * @param dmx_fd an opened demux file descriptor + * @param delivery_system delivery system to be scanned + * @param other_nit use alternate table IDs for NIT and other tables + * @param timeout_multiply improves the timeout for each table reception + * by using a value that will multiply the wait + * time. + * + * Given an opened frontend and demux, this function seeks for all programs + * available at the transport stream, and parses the following tables: + * PAT, PMT, NIT, SDT (and VCT, if the delivery system is ATSC). + * + * On sucess, it returns a pointer to a struct dvb_v5_descriptors, that can + * either be used to tune into a service or to be stored inside a file. + */ +struct dvb_v5_descriptors *dvb_get_ts_tables(struct dvb_v5_fe_parms *parms, int dmx_fd, + uint32_t delivery_system, + unsigned other_nit, + unsigned timeout_multiply); + +/** + * @brief frees a struct dvb_v5_descriptors + * @ingroup frontend_scan + * + * @param dvb_desc pointed to the structure to be freed. + * + * This function recursively frees everything that is allocated by + * dvb_get_ts_tables() and stored at dvb_desc, including dvb_desc itself. + */ +void dvb_free_ts_tables(struct dvb_v5_descriptors *dvb_desc); + +/** + * @brief Callback for the application to show the frontend status + * @ingroup frontend_scan + * + * @param args a pointer, opaque to libdvbv5, to be used by the + * application if needed. + * @param parms pointer to struct dvb_v5_fe_parms created when the + * frontend is opened + */ +typedef int (check_frontend_t)(void *args, struct dvb_v5_fe_parms *parms); + +/** + * @brief Scans a DVB dvb_add_scaned_transponder + * @ingroup frontend_scan + * + * @param parms pointer to struct dvb_v5_fe_parms created when the + * frontend is opened + * @param entry DVB file entry that corresponds to a transponder to be + * tuned + * @param dmx_fd an opened demux file descriptor + * @param check_frontend a pointer to a function that will show the frontend + * status while tuning into a transponder + * @param args a pointer, opaque to libdvbv5, that will be used when + * calling check_frontend. It should contain any parameters + * that could be needed by check_frontend. + * @param other_nit Use alternate table IDs for NIT and other tables + * @param timeout_multiply Improves the timeout for each table reception, by + * + * This is the function that applications should use when doing a transponders + * scan. It does everything needed to fill the entries with DVB programs + * (virtual channels) and detect the PIDs associated with them. + * + * A typical usage is to after open a channel file, open a dmx_fd and open + * a frontend. Then, seek for the MPEG tables on all the transponder + * frequencies with: + * + * @code + * for (entry = dvb_file->first_entry; entry != NULL; entry = entry->next) { + * struct dvb_v5_descriptors *dvb_scan_handler = NULL; + * + * dvb_scan_handler = dvb_scan_transponder(parms, entry, dmx_fd, + * &check_frontend, args, + * args->other_nit, + * args->timeout_multiply); + * if (parms->abort) { + * dvb_scan_free_handler_table(dvb_scan_handler); + * break; + * } + * if (dvb_scan_handler) { + * dvb_store_channel(&dvb_file_new, parms, dvb_scan_handler, + * args->get_detected, args->get_nit); + * dvb_scan_free_handler_table(dvb_scan_handler); + * } + * } + * @endcode + */ +struct dvb_v5_descriptors *dvb_scan_transponder(struct dvb_v5_fe_parms *parms, + struct dvb_entry *entry, + int dmx_fd, + check_frontend_t *check_frontend, + void *args, + unsigned other_nit, + unsigned timeout_multiply); + + +/** + * @brief Add new transponders to a dvb_file + * @ingroup frontend_scan + * + * @param parms pointer to struct dvb_v5_fe_parms created when the + * frontend is opened + * @param dvb_scan_handler pointer to a struct dvb_v5_descriptors containing + * scaned MPEG-TS + * @param first_entry first entry of a DVB file struct + * @param entry current entry on a DVB file struct + * + * When the NIT table is parsed, some new transponders could be described + * inside. This function adds new entries to a dvb_file struct, pointing + * to those new transponders. It is used inside the scan loop, as shown at + * the dvb_scan_transponder(), to add new channels. + * + * Example: + * @code + * for (entry = dvb_file->first_entry; entry != NULL; entry = entry->next) { + * struct dvb_v5_descriptors *dvb_scan_handler = NULL; + * + * dvb_scan_handler = dvb_scan_transponder(parms, entry, dmx_fd, + * &check_frontend, args, + * args->other_nit, + * args->timeout_multiply); + * if (parms->abort) { + * dvb_scan_free_handler_table(dvb_scan_handler); + * break; + * } + * if (dvb_scan_handler) { + * dvb_store_channel(&dvb_file_new, parms, dvb_scan_handler, + * args->get_detected, args->get_nit); + * dvb_scan_free_handler_table(dvb_scan_handler); + * + * dvb_add_scaned_transponders(parms, dvb_scan_handler, + * dvb_file->first_entry, entry); + * + * dvb_scan_free_handler_table(dvb_scan_handler); + * } + * } + * @endcode + */ +void dvb_add_scaned_transponders(struct dvb_v5_fe_parms *parms, + struct dvb_v5_descriptors *dvb_scan_handler, + struct dvb_entry *first_entry, + struct dvb_entry *entry); + +#ifndef _DOXYGEN +/* + * Some ancillary functions used internally inside the library, used to + * identify duplicated transport streams and add new found transponder entries + */ +int dvb_estimate_freq_shift(struct dvb_v5_fe_parms *parms); + +int dvb_new_freq_is_needed(struct dvb_entry *entry, struct dvb_entry *last_entry, + uint32_t freq, enum dvb_sat_polarization pol, int shift); + +struct dvb_entry *dvb_scan_add_entry(struct dvb_v5_fe_parms *parms, + struct dvb_entry *first_entry, + struct dvb_entry *entry, + uint32_t freq, uint32_t shift, + enum dvb_sat_polarization pol); + +int dvb_new_entry_is_needed(struct dvb_entry *entry, + struct dvb_entry *last_entry, + uint32_t freq, int shift, + enum dvb_sat_polarization pol, uint32_t stream_id); + +struct dvb_entry *dvb_scan_add_entry_ex(struct dvb_v5_fe_parms *parms, + struct dvb_entry *first_entry, + struct dvb_entry *entry, + uint32_t freq, uint32_t shift, + enum dvb_sat_polarization pol, + uint32_t stream_id); + +void dvb_update_transponders(struct dvb_v5_fe_parms *parms, + struct dvb_v5_descriptors *dvb_scan_handler, + struct dvb_entry *first_entry, + struct dvb_entry *entry); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/dvb-v5-std.h b/include/libdvbv5/dvb-v5-std.h new file mode 100644 index 0000000..6ca8c90 --- /dev/null +++ b/include/libdvbv5/dvb-v5-std.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2011-2014 - Mauro Carvalho Chehab + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Per-delivery system properties defined at libdvbv5 scope, following + * the same model as defined at the Linux DVB media specs: + * http://linuxtv.org/downloads/v4l-dvb-apis/FE_GET_SET_PROPERTY.html + */ +#ifndef _DVB_V5_STD_H +#define _DVB_V5_STD_H + +#include +#include "dvb-frontend.h" + +/** + * @file dvb-v5-std.h + * @ingroup frontend + * @brief Provides libdvbv5 defined properties for the frontend. + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/* + * User DTV codes, for internal usage. There are two sets of + * properties. One for DTV properties and another one for statistics + */ + +/* + * First set: DTV properties that don't belong to Kernelspace + * + * Those properties contain data that comes from the MPEG-TS + * tables, like audio/video/other PIDs, and satellite config + */ + +/** + * @def DTV_USER_COMMAND_START + * @brief Start number for libdvbv5 user commands + * @ingroup frontend + * @def DTV_POLARIZATION + * @brief Satellite polarization (for Satellite delivery systems) + * @ingroup frontend + * @def DTV_AUDIO_PID + * @brief Audio PID + * @ingroup frontend + * @def DTV_VIDEO_PID + * @brief Video PID + * @ingroup frontend + * @def DTV_SERVICE_ID + * @brief MPEG TS service ID + * @ingroup frontend + * @def DTV_CH_NAME + * @brief Digital TV service name + * @ingroup frontend + * @def DTV_VCHANNEL + * @brief Digital TV channel number. May contain symbols + * @ingroup frontend + * @def DTV_SAT_NUMBER + * @brief Number of the satellite (used on multi-dish Satellite systems) + * @ingroup frontend + * @def DTV_DISEQC_WAIT + * @brief Extra time needed to wait for DiSeqC to complete, in ms. + * The minimal wait time is 15 ms. The time here will be + * added to the minimal time. + * @ingroup frontend + * @def DTV_DISEQC_LNB + * @brief LNBf name + * @ingroup frontend + * @def DTV_FREQ_BPF + * @brief SCR/Unicable band-pass filter frequency in kHz + * @ingroup frontend + * @def DTV_PLS_CODE + * @brief DVB-T2 PLS code. Not used internally. It is needed + * only for file conversion. + * @ingroup frontend + * @def DTV_PLS_MODE + * @brief DVB-T2 PLS mode. Not used internally. It is needed + * only for file conversion. + * @ingroup frontend + * @def DTV_COUNTRY_CODE + * @brief Country variant of international delivery system standard. + in ISO 3166-1 two letter code. + * @ingroup frontend + * @def DTV_MAX_USER_COMMAND + * @brief Last user command + * @ingroup frontend + * @def DTV_USER_NAME_SIZE + * @brief Number of user commands + * @ingroup frontend + */ + +#define DTV_USER_COMMAND_START 256 + +#define DTV_POLARIZATION (DTV_USER_COMMAND_START + 0) +#define DTV_VIDEO_PID (DTV_USER_COMMAND_START + 1) +#define DTV_AUDIO_PID (DTV_USER_COMMAND_START + 2) +#define DTV_SERVICE_ID (DTV_USER_COMMAND_START + 3) +#define DTV_CH_NAME (DTV_USER_COMMAND_START + 4) +#define DTV_VCHANNEL (DTV_USER_COMMAND_START + 5) +#define DTV_SAT_NUMBER (DTV_USER_COMMAND_START + 6) +#define DTV_DISEQC_WAIT (DTV_USER_COMMAND_START + 7) +#define DTV_DISEQC_LNB (DTV_USER_COMMAND_START + 8) +#define DTV_FREQ_BPF (DTV_USER_COMMAND_START + 9) +#define DTV_PLS_CODE (DTV_USER_COMMAND_START + 10) +#define DTV_PLS_MODE (DTV_USER_COMMAND_START + 11) +#define DTV_COUNTRY_CODE (DTV_USER_COMMAND_START + 12) + +#define DTV_MAX_USER_COMMAND DTV_COUNTRY_CODE + +#define DTV_USER_NAME_SIZE (1 + DTV_MAX_USER_COMMAND - DTV_USER_COMMAND_START) + +/** + * @enum dvb_sat_polarization + * @brief Polarization types for Satellite systems + * @ingroup satellite + * + * @param POLARIZATION_OFF Polarization disabled/unused. + * @param POLARIZATION_H Horizontal polarization + * @param POLARIZATION_V Vertical polarization + * @param POLARIZATION_L Left circular polarization (C-band) + * @param POLARIZATION_R Right circular polarization (C-band) + */ +enum dvb_sat_polarization { + POLARIZATION_OFF = 0, + POLARIZATION_H = 1, + POLARIZATION_V = 2, + POLARIZATION_L = 3, + POLARIZATION_R = 4, +}; + +/* + * Second set: DTV statistics + * + * Those properties contain statistics measurements that aren't + * either provided by the Kernel via property cmd/value pair, + * like status (with has its own ioctl), or that are derivated + * measures from two or more Kernel reported stats. + */ + +/** + * @def DTV_STAT_COMMAND_START + * @brief Start number for libdvbv5 statistics commands + * @ingroup frontend + * @def DTV_STATUS + * @brief Lock status of a DTV frontend. This actually comes from + * the Kernel, but it uses a separate ioctl. + * @ingroup frontend + * @def DTV_BER + * @brief Bit Error Rate. This is a parameter that it is + * derivated from two counters at the Kernel side + * @ingroup frontend + * @def DTV_PER + * @brief Packet Error Rate. This is a parameter that it is + * derivated from two counters at the Kernel side + * @ingroup frontend + * @def DTV_QUALITY + * @brief A quality indicator that represents if a locked + * channel provides a good, OK or poor signal. This is + * estimated considering the error rates, signal strengh + * and/or S/N ratio of the carrier. + * @ingroup frontend + * @def DTV_PRE_BER + * @brief Bit Error Rate before Viterbi. This is the error rate + * before applying the Forward Error Correction. This is + * a parameter that it is derivated from two counters + * at the Kernel side. + * @ingroup frontend + * @def DTV_MAX_STAT_COMMAND + * @brief Last statistics command + * @ingroup frontend + * @def DTV_STAT_NAME_SIZE + * @brief Number of statistics commands + * @ingroup frontend + * @def DTV_NUM_KERNEL_STATS + * @brief Number of statistics commands provided by the Kernel + * @ingroup frontend + * @def DTV_NUM_STATS_PROPS + * @brief Total number of statistics commands + * @ingroup frontend + */ + +#define DTV_STAT_COMMAND_START 512 + +#define DTV_STATUS (DTV_STAT_COMMAND_START + 0) +#define DTV_BER (DTV_STAT_COMMAND_START + 1) +#define DTV_PER (DTV_STAT_COMMAND_START + 2) +#define DTV_QUALITY (DTV_STAT_COMMAND_START + 3) +#define DTV_PRE_BER (DTV_STAT_COMMAND_START + 4) + +#define DTV_MAX_STAT_COMMAND DTV_PRE_BER + +#define DTV_STAT_NAME_SIZE (1 + DTV_MAX_STAT_COMMAND - DTV_STAT_COMMAND_START) + +/* There are currently 8 stats provided on Kernelspace */ +#define DTV_NUM_KERNEL_STATS 8 + +#define DTV_NUM_STATS_PROPS (DTV_NUM_KERNEL_STATS + DTV_STAT_NAME_SIZE) + +/** + * @enum dvb_quality + * @brief Provides an estimation about the user's experience + * while watching to a given MPEG stream + * @ingroup frontend + * + * @param DVB_QUAL_UNKNOWN Quality could not be estimated, as the Kernel driver + * doesn't provide enough statistics + * + * @param DVB_QUAL_POOR The signal reception is poor. Signal loss or packets + * can be lost too frequently. + * @param DVB_QUAL_OK The signal reception is ok. Eventual artifacts could + * be expected, but it should work. + * @param DVB_QUAL_GOOD The signal is good, and not many errors are happening. + * The user should have a good experience watching the + * stream. + */ +enum dvb_quality { + DVB_QUAL_UNKNOWN = 0, + DVB_QUAL_POOR, + DVB_QUAL_OK, + DVB_QUAL_GOOD, +}; + +#ifndef _DOXYGEN + +/* + * Some tables to translate from value to string + * + * These tables are raw ways to translate from some DTV values into strings. + * Please use the API-provided function dvb_cmd_name() and dvb_dvb_attr_names(), + * instead of using the tables directly. + */ + +extern const unsigned int sys_dvbt_props[]; +extern const unsigned int sys_dvbt2_props[]; +extern const unsigned int sys_isdbt_props[]; +extern const unsigned int sys_atsc_props[]; +extern const unsigned int sys_atscmh_props[]; +extern const unsigned int sys_dvbc_annex_ac_props[]; +extern const unsigned int sys_dvbc_annex_b_props[]; +extern const unsigned int sys_dvbs_props[]; +extern const unsigned int sys_dvbs2_props[]; +extern const unsigned int sys_turbo_props[]; +extern const unsigned int sys_isdbs_props[]; +extern const unsigned int *dvb_v5_delivery_system[]; +extern const char *dvb_sat_pol_name[6]; +extern const char *dvb_user_name[DTV_USER_NAME_SIZE + 1]; +extern const char *dvb_stat_name[DTV_STAT_NAME_SIZE + 1]; +extern const void *dvb_user_attr_names[]; + +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + +#endif diff --git a/include/libdvbv5/eit.h b/include/libdvbv5/eit.h new file mode 100644 index 0000000..04b34e2 --- /dev/null +++ b/include/libdvbv5/eit.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file eit.h + * @ingroup dvb_table + * @brief Provides the table parser for the DVB EIT (Event Information Table) + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ETSI EN 300 468 + * + * @see + * http://www.etherguidesystems.com/Help/SDOs/dvb/syntax/tablesections/EIT.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _EIT_H +#define _EIT_H + +#include +#include /* ssize_t */ +#include + +#include + +/** + * @def DVB_TABLE_EIT + * @brief DVB EIT table ID for the actual TS + * @ingroup dvb_table + * @def DVB_TABLE_EIT_OTHER + * @brief DVB EIT table ID for other TS + * @ingroup dvb_table + * @def DVB_TABLE_EIT_PID + * @brief DVB EIT Program ID + * @ingroup dvb_table + * @def DVB_TABLE_EIT_SCHEDULE + * @brief Start table ID for the DVB EIT schedule data on the actual TS + * @ingroup dvb_table + * @def DVB_TABLE_EIT_SCHEDULE_OTHER + * @brief Start table ID for the DVB EIT schedule data on other TS + * @ingroup dvb_table + */ +#define DVB_TABLE_EIT 0x4E +#define DVB_TABLE_EIT_OTHER 0x4F +#define DVB_TABLE_EIT_PID 0x12 + +#define DVB_TABLE_EIT_SCHEDULE 0x50 /* - 0x5F */ +#define DVB_TABLE_EIT_SCHEDULE_OTHER 0x60 /* - 0x6F */ + +/** + * @struct dvb_table_eit_event + * @brief DVB EIT event table + * @ingroup dvb_table + * + * @param event_id an uniquelly (inside a service ID) event ID + * @param desc_length descriptor's length + * @param free_CA_mode free CA mode. 0 indicates that the event + * is not scrambled + * @param running_status running status of the event. The status can + * be translated to string via + * dvb_eit_running_status_name string table. + * @param descriptor pointer to struct dvb_desc + * @param next pointer to struct dvb_table_eit_event + * @param tm_start event start (in struct tm format) + * @param duration duration in seconds + * @param service_id service ID + * + * This structure is used to store the original EIT event table, + * converting the integer fields to the CPU endianness, and converting the + * timestamps to a way that it is better handled on Linux. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_eit_event::descriptor (including it) won't + * be bit-mapped to the data parsed from the MPEG TS. So, metadata are added + * there. + */ +struct dvb_table_eit_event { + uint16_t event_id; + union { + uint16_t bitfield1; /* first 2 bytes are MJD, they need to be bswapped */ + uint8_t dvbstart[5]; + } __attribute__((packed)); + uint8_t dvbduration[3]; + union { + uint16_t bitfield2; + struct { + uint16_t desc_length:12; + uint16_t free_CA_mode:1; + uint16_t running_status:3; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct dvb_table_eit_event *next; + struct tm start; + uint32_t duration; + uint16_t service_id; +} __attribute__((packed)); + +/** + * @struct dvb_table_eit + * @brief DVB EIT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param transport_id transport id + * @param network_id network id + * @param last_segment last segment + * @param last_table_id last table id + * @param event pointer to struct dvb_table_eit_event + * + * This structure is used to store the original EIT table, + * converting the integer fields to the CPU endianness. + * + * Everything after dvb_table_eit::event (including it) won't + * be bit-mapped to the data parsed from the MPEG TS. So, metadata are added + * there. + */ +struct dvb_table_eit { + struct dvb_table_header header; + uint16_t transport_id; + uint16_t network_id; + uint8_t last_segment; + uint8_t last_table_id; + struct dvb_table_eit_event *event; +} __attribute__((packed)); + +/** + * @brief Macro used to find event on a DVB EIT table + * @ingroup dvb_table + * + * @param _event event to seek + * @param _eit pointer to struct dvb_table_eit_event + */ +#define dvb_eit_event_foreach(_event, _eit) \ + for( struct dvb_table_eit_event *_event = _eit->event; _event; _event = _event->next ) \ + +struct dvb_v5_fe_parms; + +/** @brief Converts a running_status field into string */ +extern const char *dvb_eit_running_status_name[8]; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses EIT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the EIT raw data + * @param buflen length of the buffer + * @param table pointer to struct dvb_table_eit to be allocated and filled + * + * This function allocates an EIT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t dvb_table_eit_init (struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct dvb_table_eit **table); + +/** + * @brief Frees all data allocated by the DVB EIT table parser + * @ingroup dvb_table + * + * @param table pointer to struct dvb_table_eit to be freed + */ +void dvb_table_eit_free(struct dvb_table_eit *table); + +/** + * @brief Prints the content of the DVB EIT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_eit + */ +void dvb_table_eit_print(struct dvb_v5_fe_parms *parms, + struct dvb_table_eit *table); + +/** + * @brief Converts a DVB EIT formatted timestamp into struct tm + * @ingroup dvb_table + * + * @param data event on DVB EIT time format + * @param tm pointer to struct tm where the converted timestamp will + * be stored. + */ +void dvb_time(const uint8_t data[5], struct tm *tm); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/header.h b/include/libdvbv5/header.h new file mode 100644 index 0000000..cb528e6 --- /dev/null +++ b/include/libdvbv5/header.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * Described at ISO/IEC 13818-1 + */ + +#ifndef _HEADER_H +#define _HEADER_H + +#include +#include /* ssize_t */ + +/** + * @file header.h + * @ingroup dvb_table + * @brief Provides the MPEG TS table headers + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/** + * @struct dvb_ts_packet_header + * @brief Header of a MPEG-TS transport packet + * @ingroup dvb_table + * + * @param sync_byte sync byte + * @param pid Program ID + * @param transport_priority transport priority + * @param payload_unit_start_indicator payload unit start indicator + * @param transport_error_indicator transport error indicator + * @param continuity_counter continuity counter + * @param adaptation_field_control adaptation field control + * @param transport_scrambling_control transport scrambling control + * @param adaptation_field_length adaptation field length + * + * @see http://www.etherguidesystems.com/Help/SDOs/MPEG/Semantics/MPEG-2/transport_packet.aspx + */ +struct dvb_ts_packet_header { + uint8_t sync_byte; + union { + uint16_t bitfield; + struct { + uint16_t pid:13; + uint16_t transport_priority:1; + uint16_t payload_unit_start_indicator:1; + uint16_t transport_error_indicator:1; + } __attribute__((packed)); + } __attribute__((packed)); + uint8_t continuity_counter:4; + uint8_t adaptation_field_control:2; + uint8_t transport_scrambling_control:2; + + /* Only if adaptation_field_control > 1 */ + uint8_t adaptation_field_length; +} __attribute__((packed)); + +/** + * @struct dvb_table_header + * @brief Header of a MPEG-TS table + * @ingroup dvb_table + * + * @param table_id table id + * @param section_length section length + * @param syntax syntax + * @param id TS ID + * @param current_next current next + * @param version version + * @param section_id section number + * @param last_section last section number + * + * All MPEG-TS tables start with this header. + */ +struct dvb_table_header { + uint8_t table_id; + union { + uint16_t bitfield; + struct { + uint16_t section_length:12; + uint8_t one:2; + uint8_t zero:1; + uint8_t syntax:1; + } __attribute__((packed)); + } __attribute__((packed)); + uint16_t id; /* TS ID */ + uint8_t current_next:1; + uint8_t version:5; + uint8_t one2:2; + + uint8_t section_id; /* section_number */ + uint8_t last_section; /* last_section_number */ +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses MPEG-TS table header + * @ingroup dvb_table + * + * @param header pointer to struct dvb_table_header to be parsed + */ +void dvb_table_header_init (struct dvb_table_header *header); +/** + * @brief Prints the content of the MPEG-TS table header + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param header pointer to struct dvb_table_header to be printed + */ +void dvb_table_header_print(struct dvb_v5_fe_parms *parms, + const struct dvb_table_header *header); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/mgt.h b/include/libdvbv5/mgt.h new file mode 100644 index 0000000..c2dece0 --- /dev/null +++ b/include/libdvbv5/mgt.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file mgt.h + * @ingroup dvb_table + * @brief Provides the table parser for the ATSC MGT (Master Guide Table) + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ATSC A/65:2009 + * + * @see + * http://www.etherguidesystems.com/help/sdos/atsc/syntax/tablesections/MGT.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _MGT_H +#define _MGT_H + +#include +#include /* ssize_t */ + +#include + +/** + * @def ATSC_TABLE_MGT + * @brief ATSC MGT table ID + * @ingroup dvb_table + */ +#define ATSC_TABLE_MGT 0xC7 + +/** + * @struct atsc_table_mgt_table + * @brief ATSC tables descrition at MGT table + * @ingroup dvb_table + * + * @param type table type + * @param pid table type pid + * @param type_version type type version number + * @param size number of bytes for the table entry + * @param desc_length table type descriptors length + * @param descriptor pointer to struct dvb_desc + * @param next pointer to struct atsc_table_mgt_table + * + * This structure is used to store the original VCT channel table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after atsc_table_mgt_table::descriptor (including it) won't + * be bit-mapped * to the data parsed from the MPEG TS. So, metadata are + * added there. + */ +struct atsc_table_mgt_table { + uint16_t type; + union { + uint16_t bitfield; + struct { + uint16_t pid:13; + uint16_t one:3; + } __attribute__((packed)); + } __attribute__((packed)); + uint8_t type_version:5; + uint8_t one2:3; + uint32_t size; + union { + uint16_t bitfield2; + struct { + uint16_t desc_length:12; + uint16_t one3:4; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct atsc_table_mgt_table *next; +} __attribute__((packed)); + +/** + * @struct atsc_table_mgt + * @brief ATSC MGT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param protocol_version protocol version + * @param tables tables_defined Number of tables defined + * @param table pointer to struct atsc_table_mgt_table + * @param descriptor pointer to struct dvb_desc + * + * This structure is used to store the original MGT channel table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after atsc_table_mgt::table (including it) won't + * be bit-mapped * to the data parsed from the MPEG TS. So, metadata are + * added there. + */ +struct atsc_table_mgt { + struct dvb_table_header header; + uint8_t protocol_version; + uint16_t tables; + struct atsc_table_mgt_table *table; + struct dvb_desc *descriptor; +} __attribute__((packed)); + +/** + * @brief Macro used to find a table inside a MGT table + * + * @param _table channel to seek + * @param _mgt pointer to struct atsc_table_mgt_table + */ +#define atsc_mgt_table_foreach( _table, _mgt ) \ + for( struct atsc_table_mgt_table *_table = _mgt->table; _table; _table = _table->next ) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses MGT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the MGT raw data + * @param buflen length of the buffer + * @param table pointer to struct atsc_table_mgt to be allocated and filled + * + * This function allocates an ATSC MGT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t atsc_table_mgt_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct atsc_table_mgt **table); + +/** + * @brief Frees all data allocated by the MGT table parser + * @ingroup dvb_table + * + * @param table pointer to struct atsc_table_mgt to be freed + */ +void atsc_table_mgt_free(struct atsc_table_mgt *table); + +/** + * @brief Prints the content of the MGT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct atsc_table_mgt + */ +void atsc_table_mgt_print(struct dvb_v5_fe_parms *parms, + struct atsc_table_mgt *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/mpeg_es.h b/include/libdvbv5/mpeg_es.h new file mode 100644 index 0000000..ac3ea13 --- /dev/null +++ b/include/libdvbv5/mpeg_es.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2013-2014 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _MPEG_ES_H +#define _MPEG_ES_H + +/** + * @file mpeg_es.h + * @ingroup dvb_table + * @brief Provides the table parser for the MPEG-TS Elementary Stream + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined in ISO 13818-2 + * + * @see + * http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#include +#include /* ssize_t */ + +/** + * @def DVB_MPEG_ES_PIC_START + * @brief Picture Start + * @ingroup dvb_table + * @def DVB_MPEG_ES_USER_DATA + * @brief User Data + * @ingroup dvb_table + * @def DVB_MPEG_ES_SEQ_START + * @brief Sequence Start + * @ingroup dvb_table + * @def DVB_MPEG_ES_SEQ_EXT + * @brief Extension + * @ingroup dvb_table + * @def DVB_MPEG_ES_GOP + * @brief Group Of Pictures + * @ingroup dvb_table + * @def DVB_MPEG_ES_SLICES + * @brief Slices + * @ingroup dvb_table + */ +#define DVB_MPEG_ES_PIC_START 0x00 +#define DVB_MPEG_ES_USER_DATA 0xb2 +#define DVB_MPEG_ES_SEQ_START 0xb3 +#define DVB_MPEG_ES_SEQ_EXT 0xb5 +#define DVB_MPEG_ES_GOP 0xb8 +#define DVB_MPEG_ES_SLICES 0x01 ... 0xaf + +/** + * @struct dvb_mpeg_es_seq_start + * @brief MPEG ES Sequence header + * @ingroup dvb_table + * + * @param type DVB_MPEG_ES_SEQ_START + * @param sync Sync bytes + * @param framerate Framerate + * @param aspect Aspect ratio + * @param height Height + * @param width Width + * @param qm_nonintra Load non-intra quantizer matrix + * @param qm_intra Load intra quantizer matrix + * @param constrained Constrained parameters flag + * @param vbv VBV buffer size + * @param one Should be 1 + * @param bitrate Bitrate + */ +struct dvb_mpeg_es_seq_start { + union { + uint32_t bitfield; + struct { + uint32_t type:8; + uint32_t sync:24; + } __attribute__((packed)); + } __attribute__((packed)); + union { + uint32_t bitfield2; + struct { + uint32_t framerate:4; + uint32_t aspect:4; + uint32_t height:12; + uint32_t width:12; + } __attribute__((packed)); + } __attribute__((packed)); + union { + uint32_t bitfield3; + struct { + uint32_t qm_nonintra:1; + uint32_t qm_intra:1; + uint32_t constrained:1; + uint32_t vbv:10; // Size of video buffer verifier = 16*1024*vbv buf size + uint32_t one:1; + uint32_t bitrate:18; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @struct dvb_mpeg_es_pic_start + * @brief MPEG ES Picture start header + * @ingroup dvb_table + * + * @param type DVB_MPEG_ES_PIC_START + * @param sync Sync bytes + * @param dummy Unused + * @param vbv_delay VBV delay + * @param coding_type Frame type (enum dvb_mpeg_es_frame_t) + * @param temporal_ref Temporal sequence number + */ +struct dvb_mpeg_es_pic_start { + union { + uint32_t bitfield; + struct { + uint32_t type:8; + uint32_t sync:24; + } __attribute__((packed)); + } __attribute__((packed)); + union { + uint32_t bitfield2; + struct { + uint32_t dummy:3; + uint32_t vbv_delay:16; + uint32_t coding_type:3; + uint32_t temporal_ref:10; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @enum dvb_mpeg_es_frame_t + * @brief MPEG frame types + * @ingroup dvb_table + * + * @var DVB_MPEG_ES_FRAME_UNKNOWN + * @brief Unknown frame + * @var DVB_MPEG_ES_FRAME_I + * @brief I frame + * @var DVB_MPEG_ES_FRAME_P + * @brief P frame + * @var DVB_MPEG_ES_FRAME_B + * @brief B frame + * @var DVB_MPEG_ES_FRAME_D + * @brief D frame + */ +enum dvb_mpeg_es_frame_t +{ + DVB_MPEG_ES_FRAME_UNKNOWN, + DVB_MPEG_ES_FRAME_I, + DVB_MPEG_ES_FRAME_P, + DVB_MPEG_ES_FRAME_B, + DVB_MPEG_ES_FRAME_D +}; + +/** + * @brief Vector that translates from enum dvb_mpeg_es_frame_t to string. + * @ingroup dvb_table + */ +extern const char *dvb_mpeg_es_frame_names[5]; + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize a struct dvb_mpeg_es_seq_start from buffer + * @ingroup dvb_table + * + * @param buf Buffer + * @param buflen Length of buffer + * @param seq_start Pointer to allocated struct dvb_mpeg_es_seq_start + * + * @return If buflen too small, return -1, 0 otherwise. + * + * This function copies the length of struct dvb_mpeg_es_seq_start + * to seq_start and fixes endianness. seq_start has to be allocated + * with malloc. + */ +int dvb_mpeg_es_seq_start_init (const uint8_t *buf, ssize_t buflen, + struct dvb_mpeg_es_seq_start *seq_start); + +/** + * @brief Print details of struct dvb_mpeg_es_seq_start + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms for log functions + * @param seq_start Pointer to struct dvb_mpeg_es_seq_start to print + * + * This function prints the fields of struct dvb_mpeg_es_seq_start + */ +void dvb_mpeg_es_seq_start_print(struct dvb_v5_fe_parms *parms, + struct dvb_mpeg_es_seq_start *seq_start); + +/** + * @brief Initialize a struct dvb_mpeg_es_pic_start from buffer + * @ingroup dvb_table + * + * @param buf Buffer + * @param buflen Length of buffer + * @param pic_start Pointer to allocated structdvb_mpeg_es_pic_start + * + * @return If buflen too small, return -1, 0 otherwise. + * + * This function copies the length of struct dvb_mpeg_es_pic_start + * to pic_start and fixes endianness. seq_start has to be allocated + * with malloc. + */ +int dvb_mpeg_es_pic_start_init (const uint8_t *buf, ssize_t buflen, + struct dvb_mpeg_es_pic_start *pic_start); + +/** + * @brief Print details of struct dvb_mpeg_es_pic_start + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms for log functions + * @param pic_start Pointer to struct dvb_mpeg_es_pic_start to print + * + * This function prints the fields of struct dvb_mpeg_es_pic_start + */ +void dvb_mpeg_es_pic_start_print(struct dvb_v5_fe_parms *parms, + struct dvb_mpeg_es_pic_start *pic_start); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/mpeg_pes.h b/include/libdvbv5/mpeg_pes.h new file mode 100644 index 0000000..1bf62d7 --- /dev/null +++ b/include/libdvbv5/mpeg_pes.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2013-2014 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _MPEG_PES_H +#define _MPEG_PES_H + +/** + * @file mpeg_pes.h + * @ingroup dvb_table + * @brief Provides the table parser for the MPEG-PES Elementary Stream + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined in ISO 13818-1 + * + * @see + * http://dvd.sourceforge.net/dvdinfo/pes-hdr.html + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#include +#include /* ssize_t */ + + +/** + * @def DVB_MPEG_PES + * @brief MPEG Packetized Elementary Stream magic + * @ingroup dvb_table + * @def DVB_MPEG_PES_AUDIO + * @brief PES Audio + * @ingroup dvb_table + * @def DVB_MPEG_PES_VIDEO + * @brief PES Video + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_MAP + * @brief PES Stream map + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_PADDING + * @brief PES padding + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_PRIVATE_2 + * @brief PES private + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_ECM + * @brief PES ECM Stream + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_EMM + * @brief PES EMM Stream + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_DIRECTORY + * @brief PES Stream directory + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_DSMCC + * @brief PES DSMCC + * @ingroup dvb_table + * @def DVB_MPEG_STREAM_H222E + * @brief PES H.222.1 type E + * @ingroup dvb_table + */ + +#define DVB_MPEG_PES 0x00001 + +#define DVB_MPEG_PES_AUDIO 0xc0 ... 0xcf +#define DVB_MPEG_PES_VIDEO 0xe0 ... 0xef + +#define DVB_MPEG_STREAM_MAP 0xBC +#define DVB_MPEG_STREAM_PADDING 0xBE +#define DVB_MPEG_STREAM_PRIVATE_2 0x5F +#define DVB_MPEG_STREAM_ECM 0x70 +#define DVB_MPEG_STREAM_EMM 0x71 +#define DVB_MPEG_STREAM_DIRECTORY 0xFF +#define DVB_MPEG_STREAM_DSMCC 0x7A +#define DVB_MPEG_STREAM_H222E 0xF8 + +/** + * @struct ts_t + * @brief MPEG PES timestamp structure, used for dts and pts + * @ingroup dvb_table + * + * @param tag 4 bits Should be 0010 for PTS and 0011 for DTS + * @param bits30 3 bits Timestamp bits 30-32 + * @param one 1 bit Sould be 1 + * @param bits15 15 bits Timestamp bits 15-29 + * @param one1 1 bit Should be 1 + * @param bits00 15 Bits Timestamp bits 0-14 + * @param one2 1 bit Should be 1 + */ + +struct ts_t { + uint8_t one:1; + uint8_t bits30:3; + uint8_t tag:4; + + union { + uint16_t bitfield; + struct { + uint16_t one1:1; + uint16_t bits15:15; + } __attribute__((packed)); + } __attribute__((packed)); + + union { + uint16_t bitfield2; + struct { + uint16_t one2:1; + uint16_t bits00:15; + } __attribute__((packed)); + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @struct dvb_mpeg_pes_optional + * @brief MPEG PES optional header + * @ingroup dvb_table + * + * @param two 2 bits Should be 10 + * @param PES_scrambling_control 2 bits PES Scrambling Control (Not Scrambled=00, otherwise scrambled) + * @param PES_priority 1 bit PES Priority + * @param data_alignment_indicator 1 bit PES data alignment + * @param copyright 1 bit PES content protected by copyright + * @param original_or_copy 1 bit PES content is original (=1) or copied (=0) + * @param PTS_DTS 2 bit PES header contains PTS (=10, =11) and/or DTS (=01, =11) + * @param ESCR 1 bit PES header contains ESCR fields + * @param ES_rate 1 bit PES header contains ES_rate field + * @param DSM_trick_mode 1 bit PES header contains DSM_trick_mode field + * @param additional_copy_info 1 bit PES header contains additional_copy_info field + * @param PES_CRC 1 bit PES header contains CRC field + * @param PES_extension 1 bit PES header contains extension field + * @param length 8 bit PES header data length + * @param pts 64 bit PES PTS timestamp + * @param dts 64 bit PES DTS timestamp + */ +struct dvb_mpeg_pes_optional { + union { + uint16_t bitfield; + struct { + uint16_t PES_extension:1; + uint16_t PES_CRC:1; + uint16_t additional_copy_info:1; + uint16_t DSM_trick_mode:1; + uint16_t ES_rate:1; + uint16_t ESCR:1; + uint16_t PTS_DTS:2; + uint16_t original_or_copy:1; + uint16_t copyright:1; + uint16_t data_alignment_indicator:1; + uint16_t PES_priority:1; + uint16_t PES_scrambling_control:2; + uint16_t two:2; + } __attribute__((packed)); + } __attribute__((packed)); + uint8_t length; + uint64_t pts; + uint64_t dts; +} __attribute__((packed)); + +/** + * @struct dvb_mpeg_pes + * @brief MPEG PES data structure + * @ingroup dvb_table + * + * @param sync 24 bits DVB_MPEG_PES + * @param stream_id 8 bits PES Stream ID + * @param length 16 bits PES packet length + * @param optional Pointer to optional PES header + */ +struct dvb_mpeg_pes { + union { + uint32_t bitfield; + struct { + uint32_t stream_id:8; + uint32_t sync:24; + } __attribute__((packed)); + } __attribute__((packed)); + uint16_t length; + struct dvb_mpeg_pes_optional optional[]; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize a struct dvb_mpeg_pes from buffer + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms for log functions + * @param buf Buffer + * @param buflen Length of buffer + * @param table Pointer to allocated struct dvb_mpeg_pes + * + * @return Length of data in table + * + * This function copies the length of struct dvb_mpeg_pes + * to table and fixes endianness. The pointer table has to be + * allocated on stack or dynamically. + */ +ssize_t dvb_mpeg_pes_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, ssize_t buflen, + uint8_t *table); + +/** + * @brief Deallocate memory associated with a struct dvb_mpeg_pes + * @ingroup dvb_table + * + * @param pes struct dvb_mpeg_pes to be deallocated + * + * If the pointer pes was allocated dynamically, this function + * can be used to free the memory. + */ +void dvb_mpeg_pes_free(struct dvb_mpeg_pes *pes); + +/** + * @brief Print details of struct dvb_mpeg_pes + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms for log functions + * @param pes Pointer to struct dvb_mpeg_pes to print + * + * This function prints the fields of struct dvb_mpeg_pes + */ +void dvb_mpeg_pes_print (struct dvb_v5_fe_parms *parms, struct dvb_mpeg_pes *pes); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/mpeg_ts.h b/include/libdvbv5/mpeg_ts.h new file mode 100644 index 0000000..c31bd1d --- /dev/null +++ b/include/libdvbv5/mpeg_ts.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2013-2014 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _MPEG_TS_H +#define _MPEG_TS_H + +/** + * @file mpeg_ts.h + * @ingroup dvb_table + * @brief Provides the table parser for the MPEG-PES Elementary Stream + * @copyright GNU General Public License version 2 (GPLv2) + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined in ISO 13818-1 + * + * @see + * http://en.wikipedia.org/wiki/MPEG_transport_stream + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ +#include +#include /* ssize_t */ + +/** + * @def DVB_MPEG_TS + * @brief MPEG Transport Stream magic + * @ingroup dvb_table + * @def DVB_MPEG_TS_PACKET_SIZE + * @brief Size of an MPEG packet + * @ingroup dvb_table + */ +#define DVB_MPEG_TS 0x47 +#define DVB_MPEG_TS_PACKET_SIZE 188 + +/** + * @struct dvb_mpeg_ts_adaption + * @brief MPEG TS header adaption field + * @ingroup dvb_table + * + * @param type DVB_MPEG_ES_SEQ_START + * @param length 1 bit Adaptation Field Length + * @param discontinued 1 bit Discontinuity indicator + * @param random_access 1 bit Random Access indicator + * @param priority 1 bit Elementary stream priority indicator + * @param PCR 1 bit PCR flag + * @param OPCR 1 bit OPCR flag + * @param splicing_point 1 bit Splicing point flag + * @param private_data 1 bit Transport private data flag + * @param extension 1 bit Adaptation field extension flag + * @param data Pointer to data + */ +struct dvb_mpeg_ts_adaption { + uint8_t length; + struct { + uint8_t extension:1; + uint8_t private_data:1; + uint8_t splicing_point:1; + uint8_t OPCR:1; + uint8_t PCR:1; + uint8_t priority:1; + uint8_t random_access:1; + uint8_t discontinued:1; + } __attribute__((packed)); + uint8_t data[]; +} __attribute__((packed)); + +/** + * @struct dvb_mpeg_ts + * @brief MPEG TS header + * @ingroup dvb_table + * + * @param sync_byte DVB_MPEG_TS + * @param tei 1 bit Transport Error Indicator + * @param payload_start 1 bit Payload Unit Start Indicator + * @param priority 1 bit Transport Priority + * @param pid 13 bits Packet Identifier + * @param scrambling 2 bits Scrambling control + * @param adaptation_field 1 bit Adaptation field exist + * @param payload 1 bit Contains payload + * @param continuity_counter 4 bits Continuity counter + * @param adaption Pointer to optional adaption fiels (struct dvb_mpeg_ts_adaption) + */ +struct dvb_mpeg_ts { + uint8_t sync_byte; + union { + uint16_t bitfield; + struct { + uint16_t pid:13; + uint16_t priority:1; + uint16_t payload_start:1; + uint16_t tei:1; + } __attribute__((packed)); + } __attribute__((packed)); + struct { + uint8_t continuity_counter:4; + uint8_t payload:1; + uint8_t adaptation_field:1; + uint8_t scrambling:2; + } __attribute__((packed)); + struct dvb_mpeg_ts_adaption adaption[]; +} __attribute__((packed)); + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize a struct dvb_mpeg_ts from buffer + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms for log functions + * @param buf Buffer + * @param buflen Length of buffer + * @param table Pointer to allocated struct dvb_mpeg_ts + * @param table_length Pointer to size_t where length will be written to + * + * @return Length of data in table + * + * This function copies the length of struct dvb_mpeg_ts + * to table and fixes endianness. The pointer table has to be allocated + * on stack or dynamically. + */ +ssize_t dvb_mpeg_ts_init (struct dvb_v5_fe_parms *parms, const uint8_t *buf, ssize_t buflen, + uint8_t *table, ssize_t *table_length); + +/** + * @brief Deallocate memory associated with a struct dvb_mpeg_ts + * @ingroup dvb_table + * + * @param ts struct dvb_mpeg_ts to be deallocated + * + * If ts was allocated dynamically, this function + * can be used to free the memory. + */ +void dvb_mpeg_ts_free(struct dvb_mpeg_ts *ts); + +/** + * @brief Print details of struct dvb_mpeg_ts + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms for log functions + * @param ts Pointer to struct dvb_mpeg_ts to print + * + * This function prints the fields of struct dvb_mpeg_ts + */ +void dvb_mpeg_ts_print(struct dvb_v5_fe_parms *parms, struct dvb_mpeg_ts *ts); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/nit.h b/include/libdvbv5/nit.h new file mode 100644 index 0000000..be5d1cf --- /dev/null +++ b/include/libdvbv5/nit.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _NIT_H +#define _NIT_H + +#include +#include /* ssize_t */ + +#include +#include + +/** + * @file nit.h + * @ingroup dvb_table + * @brief Provides the descriptors for NIT MPEG-TS table + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Bug Report + * Please submit bug report and patches to linux-media@vger.kernel.org + * + * @par Relevant specs + * The table described herein is defined at: + * - ISO/IEC 13818-1 + * - ETSI EN 300 468 + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +/** + * @def DVB_TABLE_NIT + * @brief NIT table ID + * @ingroup dvb_table + * @def DVB_TABLE_NIT2 + * @brief NIT table ID (alternative table ID) + * @ingroup dvb_table + * @def DVB_TABLE_NIT_PID + * @brief NIT Program ID + * @ingroup dvb_table + */ +#define DVB_TABLE_NIT 0x40 +#define DVB_TABLE_NIT2 0x41 +#define DVB_TABLE_NIT_PID 0x10 + +/** + * @union dvb_table_nit_transport_header + * @brief MPEG-TS NIT transport header + * @ingroup dvb_table + * + * @param transport_length transport length + * + * This structure is used to store the original NIT transport header, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + */ +union dvb_table_nit_transport_header { + uint16_t bitfield; + struct { + uint16_t transport_length:12; + uint16_t reserved:4; + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @struct dvb_table_nit_transport + * @brief MPEG-TS NIT transport table + * @ingroup dvb_table + * + * @param transport_id transport id + * @param network_id network id + * @param desc_length desc length + * @param descriptor pointer to struct dvb_desc + * @param next pointer to struct dvb_table_nit_transport + * + * This structure is used to store the original NIT transport table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_nit_transport::descriptor (including it) won't + * be bit-mapped to the data parsed from the MPEG TS. So, metadata are added + * there. + */ +struct dvb_table_nit_transport { + uint16_t transport_id; + uint16_t network_id; + union { + uint16_t bitfield; + struct { + uint16_t desc_length:12; + uint16_t reserved:4; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct dvb_table_nit_transport *next; +} __attribute__((packed)); + +/** + * @struct dvb_table_nit + * @brief MPEG-TS NIT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param desc_length descriptor length + * @param descriptor pointer to struct dvb_desc + * @param transport pointer to struct dvb_table_nit_transport + * + * This structure is used to store the original NIT table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_nit::descriptor (including it) won't be bit-mapped + * to the data parsed from the MPEG TS. So, metadata are added there. + */ +struct dvb_table_nit { + struct dvb_table_header header; + union { + uint16_t bitfield; + struct { + uint16_t desc_length:12; + uint16_t reserved:4; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct dvb_table_nit_transport *transport; +} __attribute__((packed)); + +/** + * @brief typedef for a callback used when a NIT table entry is found + * @ingroup dvb_table + * + * @param nit a struct dvb_table_nit pointer + * @param desc a struct dvb_desc pointer + * @param priv an opaque optional pointer + */ +typedef void nit_handler_callback_t(struct dvb_table_nit *nit, + struct dvb_desc *desc, + void *priv); + +/** + * @brief typedef for a callback used when a NIT transport table entry is found + * @ingroup dvb_table + * + * @param nit a struct dvb_table_nit pointer + * @param tran a struct dvb_table_nit_transport pointer + * @param desc a struct dvb_desc pointer + * @param priv an opaque optional pointer + */ +typedef void nit_tran_handler_callback_t(struct dvb_table_nit *nit, + struct dvb_table_nit_transport *tran, + struct dvb_desc *desc, + void *priv); + +/** + * @brief Macro used to find a transport inside a NIT table + * @ingroup dvb_table + * + * @param _tran transport to seek + * @param _nit pointer to struct dvb_table_nit_transport + */ +#define dvb_nit_transport_foreach( _tran, _nit ) \ + for (struct dvb_table_nit_transport *_tran = _nit->transport; _tran; _tran = _tran->next) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses NIT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the NIT raw data + * @param buflen length of the buffer + * @param table pointer to struct dvb_table_nit to be allocated and filled + * + * This function allocates a NIT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t dvb_table_nit_init (struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct dvb_table_nit **table); + +/** + * @brief Frees all data allocated by the NIT table parser + * @ingroup dvb_table + * + * @param table pointer to struct dvb_table_nit to be freed + */ +void dvb_table_nit_free(struct dvb_table_nit *table); + +/** + * @brief Prints the content of the NIT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_nit + */ +void dvb_table_nit_print(struct dvb_v5_fe_parms *parms, struct dvb_table_nit *table); + +/** + * @brief For each entry at NIT and NIT transport tables, call a callback + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_nit + * @param descriptor indicates the NIT table descriptor to seek + * @param call_nit a nit_handler_callback_t function to be called when a + * new entry at the NIT table is found (or NULL). + * @param call_tran a nit_tran_handler_callback_t function to be called + * when a new entry at the NIT transport table is found + * (or NULL). + * @param priv an opaque pointer to be optionally used by the + * callbacks. The function won't touch on it, just use + * as an argument for the callback functions. + * + * When parsing a NIT entry, we need to call some code to properly handle + * when a given descriptor in the table is found. This is used, for example, + * to create newer transponders to seek during scan. + * + * For example, to seek for the CATV delivery system descriptor and call a + * function that would add a new transponder to a scan procedure: + * @code + * dvb_table_nit_descriptor_handler( + * &parms->p, dvb_scan_handler->nit, + * cable_delivery_system_descriptor, + * NULL, add_update_nit_dvbc, &tr); + * @endcode + */ +void dvb_table_nit_descriptor_handler( + struct dvb_v5_fe_parms *parms, + struct dvb_table_nit *table, + enum descriptors descriptor, + nit_handler_callback_t *call_nit, + nit_tran_handler_callback_t *call_tran, + void *priv); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/pat.h b/include/libdvbv5/pat.h new file mode 100644 index 0000000..c12f123 --- /dev/null +++ b/include/libdvbv5/pat.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file pat.h + * @ingroup dvb_table + * @brief Provides the descriptors for PAT MPEG-TS table + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ISO/IEC 13818-1 + * + * @see http://www.etherguidesystems.com/help/sdos/mpeg/syntax/tablesections/pat.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _PAT_H +#define _PAT_H + +#include +#include /* ssize_t */ + +#include + +/** + * @def DVB_TABLE_PAT + * @brief PAT table ID + * @ingroup dvb_table + * @def DVB_TABLE_PAT_PID + * @brief PAT Program ID + * @ingroup dvb_table + */ +#define DVB_TABLE_PAT 0x00 +#define DVB_TABLE_PAT_PID 0x0000 + +/** + * @struct dvb_table_pat_program + * @brief MPEG-TS PAT program table + * @ingroup dvb_table + * + * @param service_id service id + * @param pid pid + * @param next pointer to struct dvb_table_pat_program + * + * This structure is used to store the original PAT program table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_pat_program::next (including it) won't be bit-mapped + * to the data parsed from the MPEG TS. So, metadata are added there. + */ +struct dvb_table_pat_program { + uint16_t service_id; + union { + uint16_t bitfield; + struct { + uint16_t pid:13; + uint8_t reserved:3; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_table_pat_program *next; +} __attribute__((packed)); + +/** + * @struct dvb_table_pat + * @brief MPEG-TS PAT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param programs number of programs + * @param program pointer to struct dvb_table_pat_program + + * This structure is used to store the original PAT table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_pat_program::program (including it) won't be bit-mapped + * to the data parsed from the MPEG TS. So, metadata are added there. + */ +struct dvb_table_pat { + struct dvb_table_header header; + uint16_t programs; + struct dvb_table_pat_program *program; +} __attribute__((packed)); + +/** + * @brief Macro used to find programs on a PAT table + * @ingroup dvb_table + * + * @param _pgm program to seek + * @param _pat pointer to struct dvb_table_pat_program + */ +#define dvb_pat_program_foreach(_pgm, _pat) \ + for (struct dvb_table_pat_program *_pgm = _pat->program; _pgm; _pgm = _pgm->next) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses PAT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the PAT raw data + * @param buflen length of the buffer + * @param table pointer to struct dvb_table_pat to be allocated and filled + * + * This function allocates a PAT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t dvb_table_pat_init (struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct dvb_table_pat **table); + +/** + * @brief Frees all data allocated by the PAT table parser + * @ingroup dvb_table + * + * @param table pointer to struct dvb_table_pat to be freed + */ +void dvb_table_pat_free(struct dvb_table_pat *table); + +/** + * @brief Prints the content of the PAT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_pat + */ +void dvb_table_pat_print(struct dvb_v5_fe_parms *parms, + struct dvb_table_pat *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/pmt.h b/include/libdvbv5/pmt.h new file mode 100644 index 0000000..e043b71 --- /dev/null +++ b/include/libdvbv5/pmt.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file pmt.h + * @ingroup dvb_table + * @brief Provides the descriptors for PMT MPEG-TS table + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ISO/IEC 13818-1 + * + * @see http://www.etherguidesystems.com/help/sdos/mpeg/syntax/tablesections/pmts.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _PMT_H +#define _PMT_H + +#include +#include /* ssize_t */ + +#include + +/** + * @def DVB_TABLE_PMT + * @brief PMT table ID + * @ingroup dvb_table + */ +#define DVB_TABLE_PMT 0x02 + +/** + * @enum dvb_streams + * @brief Add support for MPEG-TS Stream types + * @ingroup dvb_table + * + * @var stream_reserved0 + * @brief ITU-T | ISO/IEC Reserved + * @var stream_video + * @brief ISO/IEC 11172 Video + * @var stream_video_h262 + * @brief ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream + * @var stream_audio + * @brief ISO/IEC 11172 Audio + * @var stream_audio_13818_3 + * @brief ISO/IEC 13818-3 Audio + * @var stream_private_sections + * @brief ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections + * @var stream_private_data + * @brief ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data + * @var stream_mheg + * @brief ISO/IEC 13522 MHEG + * @var stream_h222 + * @brief ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC + * @var stream_h222_1 + * @brief ITU-T Rec. H.222.1 + * @var stream_13818_6_A + * @brief ISO/IEC 13818-6 type A + * @var stream_13818_6_B + * @brief ISO/IEC 13818-6 type B + * @var stream_13818_6_C + * @brief ISO/IEC 13818-6 type C + * @var stream_13818_6_D + * @brief ISO/IEC 13818-6 type D + * @var stream_h222_aux + * @brief ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary + * @var stream_audio_adts + * @brief ISO/IEC 13818-7 Audio with ADTS transport syntax + * @var stream_video_14496_2 + * @brief ISO/IEC 14496-2 Visual + * @var stream_audio_latm + * @brief ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 / AMD 1 + * @var stream_14496_1_pes + * @brief ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets + * @var stream_14496_1_iso + * @brief ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC14496_sections. + * @var stream_download + * @brief ISO/IEC 13818-6 Synchronized Download Protocol + * @var stream_reserved + * @brief ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved (from 0x15 to 0x7f) + * @var stream_private + * @brief User Private (from 0x80 to 0xff) + */ +enum dvb_streams { + stream_reserved0 = 0x00, + stream_video = 0x01, + stream_video_h262 = 0x02, + stream_audio = 0x03, + stream_audio_13818_3 = 0x04, + stream_private_sections = 0x05, + stream_private_data = 0x06, + stream_mheg = 0x07, + stream_h222 = 0x08, + stream_h222_1 = 0x09, + stream_13818_6_A = 0x0A, + stream_13818_6_B = 0x0B, + stream_13818_6_C = 0x0C, + stream_13818_6_D = 0x0D, + stream_h222_aux = 0x0E, + stream_audio_adts = 0x0F, + stream_video_14496_2 = 0x10, + stream_audio_latm = 0x11, + stream_14496_1_pes = 0x12, + stream_14496_1_iso = 0x13, + stream_download = 0x14, + stream_reserved = 0x15, + stream_private = 0x80 +}; + +/** + * @brief Converts from enum dvb_streams into a string + * @ingroup dvb_table + */ +extern const char *pmt_stream_name[]; + +/** + * @struct dvb_table_pmt_stream + * @brief MPEG-TS PMT stream table + * @ingroup dvb_table + * + * @param type stream type + * @param elementary_pid elementary pid + * @param desc_length descriptor length + * @param zero zero + * @param descriptor pointer to struct dvb_desc + * @param next pointer to struct dvb_table_pmt_stream + + * + * This structure is used to store the original PMT stream table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_pmt_stream::descriptor (including it) won't be + * bit-mapped to the data parsed from the MPEG TS. So, metadata are added there. + */ +struct dvb_table_pmt_stream { + uint8_t type; + union { + uint16_t bitfield; + struct { + uint16_t elementary_pid:13; + uint16_t reserved:3; + } __attribute__((packed)); + } __attribute__((packed)); + union { + uint16_t bitfield2; + struct { + uint16_t desc_length:10; + uint16_t zero:2; + uint16_t reserved2:4; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct dvb_table_pmt_stream *next; +} __attribute__((packed)); + +/** + * @struct dvb_table_pmt + * @brief MPEG-TS PMT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param pcr_pid PCR PID + * @param desc_length descriptor length + * @param descriptor pointer to struct dvb_desc + * @param stream pointer to struct dvb_table_pmt_stream + * + * This structure is used to store the original PMT stream table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_pmt::descriptor (including it) won't be + * bit-mapped to the data parsed from the MPEG TS. So, metadata are added there. + */ +struct dvb_table_pmt { + struct dvb_table_header header; + union { + uint16_t bitfield; + struct { + uint16_t pcr_pid:13; + uint16_t reserved2:3; + } __attribute__((packed)); + } __attribute__((packed)); + + union { + uint16_t bitfield2; + struct { + uint16_t desc_length:10; + uint16_t zero3:2; + uint16_t reserved3:4; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct dvb_table_pmt_stream *stream; +} __attribute__((packed)); + +/** @brief First field at the struct */ +#define dvb_pmt_field_first header + +/** @brief First field that are not part of the received data */ +#define dvb_pmt_field_last descriptor + +/** + * @brief Macro used to find streams on a PMT table + * @ingroup dvb_table + * + * @param _stream stream to seek + * @param _pmt pointer to struct dvb_table_pmt_stream + */ +#define dvb_pmt_stream_foreach(_stream, _pmt) \ + for (struct dvb_table_pmt_stream *_stream = _pmt->stream; _stream; _stream = _stream->next) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses PMT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the PMT raw data + * @param buflen length of the buffer + * @param table pointer to struct dvb_table_pmt to be allocated and filled + * + * This function allocates a PMT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t dvb_table_pmt_init (struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct dvb_table_pmt **table); + +/** + * @brief Frees all data allocated by the PMT table parser + * @ingroup dvb_table + * + * @param table pointer to struct dvb_table_pmt to be freed + */ +void dvb_table_pmt_free(struct dvb_table_pmt *table); + +/** + * @brief Prints the content of the PAT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_pmt + */ +void dvb_table_pmt_print(struct dvb_v5_fe_parms *parms, + const struct dvb_table_pmt *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/sdt.h b/include/libdvbv5/sdt.h new file mode 100644 index 0000000..e54683a --- /dev/null +++ b/include/libdvbv5/sdt.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2011-2012 - Mauro Carvalho Chehab + * Copyright (c) 2012 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +#ifndef _SDT_H +#define _SDT_H + +/** + * @file sdt.h + * @ingroup dvb_table + * @brief Provides the descriptors for SDT MPEG-TS table + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ISO/IEC 13818-1 + * + * @see http://www.etherguidesystems.com/Help/SDOs/dvb/syntax/tablesections/SDT.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#include +#include /* ssize_t */ + +#include + +/** + * @def DVB_TABLE_SDT + * @brief SDT table ID + * @ingroup dvb_table + * @def DVB_TABLE_SDT2 + * @brief SDT table ID (alternative table ID) + * @ingroup dvb_table + * @def DVB_TABLE_SDT_PID + * @brief SDT Program ID + * @ingroup dvb_table + */ +#define DVB_TABLE_SDT 0x42 +#define DVB_TABLE_SDT2 0x46 +#define DVB_TABLE_SDT_PID 0x0011 + +/** + * @struct dvb_table_sdt_service + * @brief MPEG-TS SDT service table + * @ingroup dvb_table + * + * @param service_id service id + * @param EIT_present_following EIT present following + * @param EIT_schedule EIT schedule + * @param desc_length desc length + * @param free_CA_mode free CA mode + * @param running_status running status + * @param descriptor pointer to struct dvb_desc + * @param next pointer to struct dvb_table_sdt_service + * + * This structure is used to store the original SDT service table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_sdt_service::descriptor (including it) won't + * be bit-mapped to the data parsed from the MPEG TS. So, metadata are added + * there. + */ +struct dvb_table_sdt_service { + uint16_t service_id; + uint8_t EIT_present_following:1; + uint8_t EIT_schedule:1; + uint8_t reserved:6; + union { + uint16_t bitfield; + struct { + uint16_t desc_length:12; + uint16_t free_CA_mode:1; + uint16_t running_status:3; + } __attribute__((packed)); + } __attribute__((packed)); + struct dvb_desc *descriptor; + struct dvb_table_sdt_service *next; +} __attribute__((packed)); + +/** + * @struct dvb_table_sdt + * @brief MPEG-TS SDT table + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param network_id network id + * @param service pointer to struct dvb_table_sdt_service + * + * This structure is used to store the original SDT table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after dvb_table_sdt::service (including it) won't be bit-mapped + * to the data parsed from the MPEG TS. So, metadata are added there. + */ +struct dvb_table_sdt { + struct dvb_table_header header; + uint16_t network_id; + uint8_t reserved; + struct dvb_table_sdt_service *service; +} __attribute__((packed)); + +/** + * @brief Macro used to find services on a SDT table + * @ingroup dvb_table + * + * @param _service service to seek + * @param _sdt pointer to struct dvb_table_sdt_service + */ +#define dvb_sdt_service_foreach(_service, _sdt) \ + for (struct dvb_table_sdt_service *_service = _sdt->service; _service; _service = _service->next ) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses SDT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the SDT raw data + * @param buflen length of the buffer + * @param table pointer to struct dvb_table_sdt to be allocated and filled + * + * This function allocates a SDT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t dvb_table_sdt_init (struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct dvb_table_sdt **table); + +/** + * @brief Frees all data allocated by the SDT table parser + * @ingroup dvb_table + * + * @param table pointer to struct dvb_table_sdt to be freed + */ +void dvb_table_sdt_free(struct dvb_table_sdt *table); + +/** + * @brief Prints the content of the SDT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct dvb_table_sdt + */ +void dvb_table_sdt_print(struct dvb_v5_fe_parms *parms, struct dvb_table_sdt *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libdvbv5/vct.h b/include/libdvbv5/vct.h new file mode 100644 index 0000000..b79e132 --- /dev/null +++ b/include/libdvbv5/vct.h @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2013 - Mauro Carvalho Chehab + * Copyright (c) 2013 - Andre Roth + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + */ + +/** + * @file vct.h + * @ingroup dvb_table + * @brief Provides the descriptors for TVCT and CVCT tables + * @copyright GNU General Public License version 2 (GPLv2) + * @author Mauro Carvalho Chehab + * @author Andre Roth + * + * @par Relevant specs + * The table described herein is defined at: + * - ATSC A/65:2009 + * + * @see http://www.etherguidesystems.com/help/sdos/atsc/syntax/tablesections/TVCT.aspx + * @see http://www.etherguidesystems.com/help/sdos/atsc/syntax/tablesections/CVCT.aspx + * + * @par Bug Report + * Please submit bug reports and patches to linux-media@vger.kernel.org + */ + +#ifndef _VCT_H +#define _VCT_H + +#include +#include /* ssize_t */ + +#include + +/** + * @def ATSC_TABLE_TVCT + * @brief TVCT table ID + * @ingroup dvb_table + * @def ATSC_TABLE_CVCT + * @brief CVCT table ID + * @ingroup dvb_table + * @def ATSC_TABLE_VCT_PID + * @brief Program ID with the VCT tables on it + * @ingroup dvb_table + */ +#define ATSC_TABLE_TVCT 0xc8 +#define ATSC_TABLE_CVCT 0xc9 +#define ATSC_TABLE_VCT_PID 0x1ffb + +/** + * @struct atsc_table_vct_channel + * @brief ATSC VCT channel table (covers both CVCT and TVCT) + * @ingroup dvb_table + * + * @param modulation_mode modulation mode + * @param minor_channel_number minor channel number + * @param major_channel_number major channel number + * @param carrier_frequency carrier frequency + * @param channel_tsid channel tsid + * @param program_number program number + * @param service_type service type + * @param hide_guide hide guide + * @param out_of_band out of band (CVCT only) + * @param path_select path select (CVCT only) + * @param hidden hidden + * @param access_controlled access controlled + * @param ETM_location ETM location + * @param source_id source ID + * @param descriptors_length length of the descriptors + * + * @param descriptor pointer to struct dvb_desc + * @param next pointer to another struct atsc_table_vct_channel + * @param descriptors_length length of the descriptors + * @param short_name short name. The __short_name is converted + * from UTF-16 to locale charset when parsed + * + * This structure is used to store the original VCT channel table, + * converting the integer fields to the CPU endianness. + * + * The undocumented parameters are used only internally by the API and/or + * are fields that are reserved. They shouldn't be used, as they may change + * on future API releases. + * + * Everything after atsc_table_vct_channel::descriptor (including it) won't + * be bit-mapped * to the data parsed from the MPEG TS. So, metadata are + * added there. + */ +struct atsc_table_vct_channel { + uint16_t __short_name[7]; + + union { + uint32_t bitfield1; + struct { + uint32_t modulation_mode:8; + uint32_t minor_channel_number:10; + uint32_t major_channel_number:10; + uint32_t reserved1:4; + } __attribute__((packed)); + } __attribute__((packed)); + + uint32_t carrier_frequency; + uint16_t channel_tsid; + uint16_t program_number; + union { + uint16_t bitfield2; + struct { + uint16_t service_type:6; + uint16_t reserved2:3; + uint16_t hide_guide:1; + uint16_t out_of_band:1; /* CVCT only */ + uint16_t path_select:1; /* CVCT only */ + uint16_t hidden:1; + uint16_t access_controlled:1; + uint16_t ETM_location:2; + + } __attribute__((packed)); + } __attribute__((packed)); + + uint16_t source_id; + union { + uint16_t bitfield3; + struct { + uint16_t descriptors_length:10; + uint16_t reserved3:6; + } __attribute__((packed)); + } __attribute__((packed)); + + /* + * Everything after atsc_table_vct_channel::descriptor (including it) + * won't be bit-mapped to the data parsed from the MPEG TS. So, + * metadata are added there + */ + struct dvb_desc *descriptor; + struct atsc_table_vct_channel *next; + + /* The channel_short_name is converted to locale charset by vct.c */ + + char short_name[32]; +} __attribute__((packed)); + +/** + * @struct atsc_table_vct + * @brief ATSC VCT table (covers both CVCT and TVCT) + * @ingroup dvb_table + * + * @param header struct dvb_table_header content + * @param protocol_version protocol version + * @param num_channels_in_section num channels in section + * @param channel pointer to struct channel + * @param descriptor pointer to struct descriptor + * + * Everything after atsc_table_vct::channel (including it) won't be bit-mapped + * to the data parsed from the MPEG TS. So, metadata are added there + */ +struct atsc_table_vct { + struct dvb_table_header header; + uint8_t protocol_version; + + uint8_t num_channels_in_section; + + struct atsc_table_vct_channel *channel; + struct dvb_desc *descriptor; +} __attribute__((packed)); + +/** + * @union atsc_table_vct_descriptor_length + * @brief ATSC VCT descriptor length + * @ingroup dvb_table + * + * @param descriptor_length descriptor length + * + * Used internally by the library to parse the descriptor length endianness. + */ +union atsc_table_vct_descriptor_length { + uint16_t bitfield; + struct { + uint16_t descriptor_length:10; + uint16_t reserved:6; + } __attribute__((packed)); +} __attribute__((packed)); + +/** + * @brief Macro used to find channels on a VCT table + * @ingroup dvb_table + * + * @param _channel channel to seek + * @param _vct pointer to struct atsc_table_vct_channel + */ + #define atsc_vct_channel_foreach(_channel, _vct) \ + for (struct atsc_table_vct_channel *_channel = _vct->channel; _channel; _channel = _channel->next) \ + +struct dvb_v5_fe_parms; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes and parses VCT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param buf buffer containing the VCT raw data + * @param buflen length of the buffer + * @param table pointer to struct atsc_table_vct to be allocated and filled + * + * This function allocates an ATSC VCT table and fills the fields inside + * the struct. It also makes sure that all fields will follow the CPU + * endianness. Due to that, the content of the buffer may change. + * + * @return On success, it returns the size of the allocated struct. + * A negative value indicates an error. + */ +ssize_t atsc_table_vct_init(struct dvb_v5_fe_parms *parms, const uint8_t *buf, + ssize_t buflen, struct atsc_table_vct **table); +/** + * @brief Frees all data allocated by the VCT table parser + * @ingroup dvb_table + * + * @param table pointer to struct atsc_table_vct to be freed + */ +void atsc_table_vct_free(struct atsc_table_vct *table); +/** + * @brief Prints the content of the VCT table + * @ingroup dvb_table + * + * @param parms struct dvb_v5_fe_parms pointer to the opened device + * @param table pointer to struct atsc_table_vct + */ +void atsc_table_vct_print(struct dvb_v5_fe_parms *parms, + struct atsc_table_vct *table); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libv4l-plugin.h b/include/libv4l-plugin.h new file mode 100644 index 0000000..aeb2984 --- /dev/null +++ b/include/libv4l-plugin.h @@ -0,0 +1,45 @@ +/* +* Copyright (C) 2010 Nokia Corporation + +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License, version 2.1, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __LIBV4L_PLUGIN_H +#define __LIBV4L_PLUGIN_H + +#include + +/* Structure libv4l_dev_ops holds the calls from libv4ls to video nodes. + They can be normal open/close/ioctl etc. or any of them may be replaced + with a callback by a loaded plugin. +*/ + +struct libv4l_dev_ops { + void * (*init)(int fd); + void (*close)(void *dev_ops_priv); + int (*ioctl)(void *dev_ops_priv, int fd, unsigned long int request, void *arg); + ssize_t (*read)(void *dev_ops_priv, int fd, void *buffer, size_t n); + ssize_t (*write)(void *dev_ops_priv, int fd, const void *buffer, size_t n); + /* For future plugin API extension, plugins implementing the current API + must set these all to NULL, as future versions may check for these */ + void (*reserved1)(void); + void (*reserved2)(void); + void (*reserved3)(void); + void (*reserved4)(void); + void (*reserved5)(void); + void (*reserved6)(void); + void (*reserved7)(void); +}; + +#endif diff --git a/include/libv4l1-videodev.h b/include/libv4l1-videodev.h new file mode 100644 index 0000000..b67c929 --- /dev/null +++ b/include/libv4l1-videodev.h @@ -0,0 +1,207 @@ +/* libv4l1 linux/videodev.h replacement file */ +#ifndef __LINUX_VIDEODEV_H +#define __LINUX_VIDEODEV_H + +#ifdef linux +#include +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#endif + +#include + +#define VID_TYPE_CAPTURE 1 /* Can capture */ +#define VID_TYPE_TUNER 2 /* Can tune */ +#define VID_TYPE_TELETEXT 4 /* Does teletext */ +#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */ +#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */ +#define VID_TYPE_CLIPPING 32 /* Can clip */ +#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */ +#define VID_TYPE_SCALES 128 /* Scalable */ +#define VID_TYPE_MONOCHROME 256 /* Monochrome only */ +#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */ +#define VID_TYPE_MPEG_DECODER 1024 /* Can decode MPEG streams */ +#define VID_TYPE_MPEG_ENCODER 2048 /* Can encode MPEG streams */ +#define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ +#define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ + +struct video_capability +{ + char name[32]; + int type; + int channels; /* Num channels */ + int audios; /* Num audio devices */ + int maxwidth; /* Supported width */ + int maxheight; /* And height */ + int minwidth; /* Supported width */ + int minheight; /* And height */ +}; + + +struct video_channel +{ + int channel; + char name[32]; + int tuners; + uint32_t flags; +#define VIDEO_VC_TUNER 1 /* Channel has a tuner */ +#define VIDEO_VC_AUDIO 2 /* Channel has audio */ + uint16_t type; +#define VIDEO_TYPE_TV 1 +#define VIDEO_TYPE_CAMERA 2 + uint16_t norm; /* Norm set by channel */ +}; + +struct video_tuner +{ + int tuner; + char name[32]; + unsigned long rangelow, rangehigh; /* Tuner range */ + uint32_t flags; +#define VIDEO_TUNER_PAL 1 +#define VIDEO_TUNER_NTSC 2 +#define VIDEO_TUNER_SECAM 4 +#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */ +#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */ +#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */ +#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */ +#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */ + uint16_t mode; /* PAL/NTSC/SECAM/OTHER */ +#define VIDEO_MODE_PAL 0 +#define VIDEO_MODE_NTSC 1 +#define VIDEO_MODE_SECAM 2 +#define VIDEO_MODE_AUTO 3 + uint16_t signal; /* Signal strength 16bit scale */ +}; + +struct video_picture +{ + uint16_t brightness; + uint16_t hue; + uint16_t colour; + uint16_t contrast; + uint16_t whiteness; /* Black and white only */ + uint16_t depth; /* Capture depth */ + uint16_t palette; /* Palette in use */ +#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */ +#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */ +#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */ +#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */ +#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */ +#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */ +#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */ +#define VIDEO_PALETTE_YUYV 8 +#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */ +#define VIDEO_PALETTE_YUV420 10 +#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */ +#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */ +#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */ +#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */ +#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */ +#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */ +#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */ +#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */ +}; + +struct video_audio +{ + int audio; /* Audio channel */ + uint16_t volume; /* If settable */ + uint16_t bass, treble; + uint32_t flags; +#define VIDEO_AUDIO_MUTE 1 +#define VIDEO_AUDIO_MUTABLE 2 +#define VIDEO_AUDIO_VOLUME 4 +#define VIDEO_AUDIO_BASS 8 +#define VIDEO_AUDIO_TREBLE 16 +#define VIDEO_AUDIO_BALANCE 32 + char name[16]; +#define VIDEO_SOUND_MONO 1 +#define VIDEO_SOUND_STEREO 2 +#define VIDEO_SOUND_LANG1 4 +#define VIDEO_SOUND_LANG2 8 + uint16_t mode; + uint16_t balance; /* Stereo balance */ + uint16_t step; /* Step actual volume uses */ +}; + +struct video_clip +{ + int32_t x,y; + int32_t width, height; + struct video_clip *next; /* For user use/driver use only */ +}; + +struct video_window +{ + uint32_t x,y; /* Position of window */ + uint32_t width,height; /* Its size */ + uint32_t chromakey; + uint32_t flags; + struct video_clip *clips; /* Set only */ + int clipcount; +#define VIDEO_WINDOW_INTERLACE 1 +#define VIDEO_WINDOW_CHROMAKEY 16 /* Overlay by chromakey */ +#define VIDEO_CLIP_BITMAP -1 +/* bitmap is 1024x625, a '1' bit represents a clipped pixel */ +#define VIDEO_CLIPMAP_SIZE (128 * 625) +}; + +struct video_buffer +{ + void *base; + int height,width; + int depth; + int bytesperline; +}; + +struct video_mmap +{ + unsigned int frame; /* Frame (0 - n) for double buffer */ + int height,width; + unsigned int format; /* should be VIDEO_PALETTE_* */ +}; + +struct video_mbuf +{ + int size; /* Total memory to map */ + int frames; /* Frames */ + int offsets[32]; +}; + +struct vbi_format { + uint32_t sampling_rate; /* in Hz */ + uint32_t samples_per_line; + uint32_t sample_format; /* VIDEO_PALETTE_RAW only (1 byte) */ + int32_t start[2]; /* starting line for each frame */ + uint32_t count[2]; /* count of lines for each frame */ + uint32_t flags; +#define VBI_UNSYNC 1 /* can distingues between top/bottom field */ +#define VBI_INTERLACED 2 /* lines are interlaced */ +}; + +#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */ +#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */ +#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */ +#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */ +#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */ +#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */ +#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */ +#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */ +#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Get the video overlay window */ +#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */ +#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */ +#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */ +#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */ +#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ +#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ +#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ +#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */ +#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */ +#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */ +#define VIDIOCGVBIFMT _IOR('v',28, struct vbi_format) /* Get VBI information */ +#define VIDIOCSVBIFMT _IOW('v',29, struct vbi_format) /* Set VBI information */ + +#endif diff --git a/include/libv4l1.h b/include/libv4l1.h new file mode 100644 index 0000000..792d948 --- /dev/null +++ b/include/libv4l1.h @@ -0,0 +1,74 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA +*/ + +#ifndef __LIBV4L1_H +#define __LIBV4L1_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include +#include + +#if HAVE_VISIBILITY +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +/* Point this to a FILE opened for writing when you want to log error and + status messages to a file, when NULL errors will get send to stderr */ +LIBV4L_PUBLIC extern FILE *v4l1_log_file; + +/* Just like your regular open/close/etc, except that when opening a v4l2 + capture only device, full v4l1 emulation is done including emulating the + often not implemented in v4l2 drivers CGMBUF ioctl and v4l1 style mmap call + in userspace. + + Format conversion is done if necessary when capturing. That is if you + (try to) set a capture format which is not supported by the cam, but is + supported by libv4lconvert then SPICT will succeed and on SYNC / read the + data will be converted for you and returned in the request format. + + Note that currently libv4l1 depends on the kernel v4l1 compatibility layer + for: 1) Devices which are not capture only, 2) Emulation of many basic + v4l1 ioctl's which require no driver specific handling. + + Note that no functionality is added to v4l1 devices, so if for example an + obscure v4l1 device is opened which only supports some weird capture format + then libv4l1 will not be of any help (in this case it would be best to get + the driver converted to v4l2, as v4l2 has been designed to include weird + capture formats, like hw specific bayer compression methods). +*/ + +LIBV4L_PUBLIC int v4l1_open(const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l1_close(int fd); +LIBV4L_PUBLIC int v4l1_dup(int fd); +LIBV4L_PUBLIC int v4l1_ioctl(int fd, unsigned long int request, ...); +LIBV4L_PUBLIC ssize_t v4l1_read(int fd, void *buffer, size_t n); +LIBV4L_PUBLIC void *v4l1_mmap(void *start, size_t length, int prot, int flags, + int fd, int64_t offset); +LIBV4L_PUBLIC int v4l1_munmap(void *_start, size_t length); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/include/libv4l2.h b/include/libv4l2.h new file mode 100644 index 0000000..5c963f1 --- /dev/null +++ b/include/libv4l2.h @@ -0,0 +1,115 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA +*/ + +#ifndef __LIBV4L2_H +#define __LIBV4L2_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if HAVE_VISIBILITY +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +/* Point this to a FILE opened for writing when you want to log error and + status messages to a file, when NULL errors will get send to stderr */ +LIBV4L_PUBLIC extern FILE *v4l2_log_file; + +/* Just like your regular open/close/etc, except that format conversion is + done if necessary when capturing. That is if you (try to) set a capture + format which is not supported by the cam, but is supported by libv4lconvert, + then the try_fmt / set_fmt will succeed as if the cam supports the format + and on dqbuf / read the data will be converted for you and returned in + the request format. enum_fmt will also report support for the formats to + which conversion is possible. + + Another difference is that you can make v4l2_read() calls even on devices + which do not support the regular read() method. + + Note the device name passed to v4l2_open must be of a video4linux2 device, + if it is anything else (including a video4linux1 device), v4l2_open will + fail. + + Note that the argument to v4l2_ioctl after the request must be a valid + memory address of structure of the appropriate type for the request (for + v4l2 requests which expect a structure address). Passing in NULL or an + invalid memory address will not lead to failure with errno being EFAULT, + as it would with a real ioctl, but will cause libv4l2 to break, and you + get to keep both pieces. +*/ + +LIBV4L_PUBLIC int v4l2_open(const char *file, int oflag, ...); +LIBV4L_PUBLIC int v4l2_close(int fd); +LIBV4L_PUBLIC int v4l2_dup(int fd); +LIBV4L_PUBLIC int v4l2_ioctl(int fd, unsigned long int request, ...); +LIBV4L_PUBLIC ssize_t v4l2_read(int fd, void *buffer, size_t n); +LIBV4L_PUBLIC ssize_t v4l2_write(int fd, const void *buffer, size_t n); +LIBV4L_PUBLIC void *v4l2_mmap(void *start, size_t length, int prot, int flags, + int fd, int64_t offset); +LIBV4L_PUBLIC int v4l2_munmap(void *_start, size_t length); + + +/* Misc utility functions */ + +/* This function takes a value of 0 - 65535, and then scales that range to + the actual range of the given v4l control id, and then if the cid exists + and is not locked sets the cid to the scaled value. + + Normally returns 0, even if the cid did not exist or was locked, returns + non 0 when an other error occured. */ +LIBV4L_PUBLIC int v4l2_set_control(int fd, int cid, int value); + +/* This function returns a value of 0 - 65535, scaled to from the actual range + of the given v4l control id. When the cid does not exist, or could not be + accessed -1 is returned. */ +LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid); + + +/* "low level" access functions, these functions allow somewhat lower level + access to libv4l2 (currently there only is v4l2_fd_open here) */ + +/* Flags for v4l2_fd_open's v4l2_flags argument */ + +/* Disable all format conversion done by libv4l2, this includes the software + whitebalance, gamma correction, flipping, etc. libv4lconvert does. Use this + if you want raw frame data, but still want the additional error checks and + the read() emulation libv4l2 offers. */ +#define V4L2_DISABLE_CONVERSION 0x01 +/* This flag is *OBSOLETE*, since version 0.5.98 libv4l *always* reports + emulated formats to ENUM_FMT, except when conversion is disabled. */ +#define V4L2_ENABLE_ENUM_FMT_EMULATION 0x02 + +/* v4l2_fd_open: open an already opened fd for further use through + v4l2lib and possibly modify libv4l2's default behavior through the + v4l2_flags argument. + + Returns fd on success, -1 if the fd is not suitable for use through libv4l2 + (note the fd is left open in this case). */ +LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/include/libv4l2rds.h b/include/libv4l2rds.h new file mode 100644 index 0000000..2d83eec --- /dev/null +++ b/include/libv4l2rds.h @@ -0,0 +1,351 @@ +/* + * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * Author: Konke Radlow + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifndef __LIBV4L2RDS +#define __LIBV4L2RDS + + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if HAVE_VISIBILITY +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +/* used to define the current version (version field) of the v4l2_rds struct */ +#define V4L2_RDS_VERSION (2) + +/* Constants used to define the size of arrays used to store RDS information */ +#define MAX_ODA_CNT 18 /* there are 16 groups each with type a or b. Of these + * 32 distinct groups, 18 can be used for ODA purposes */ +#define MAX_AF_CNT 25 /* AF Method A allows a maximum of 25 AFs to be defined + * AF Method B does not impose a limit on the number of AFs + * but it is not fully supported at the moment and will + * not receive more than 25 AFs */ +#define MAX_TMC_ADDITIONAL 28 /* 28 is the maximal possible number of fields. + * Additional data is limited to 112 bit, and the smallest + * optional tuple has a size of 4 bit (4 bit identifier + + * 0 bits of data) */ +#define MAX_TMC_ALT_STATIONS 32 /* defined by ISO 14819-1:2003, 7.5.3.3 */ +#define MAX_TMC_AF_CNT 4 /* limit for the numbers of AFs stored per alternative TMC + * station. This value is not defined by the standard, but based on observation + * of real-world RDS-TMC streams. The maximum encountered number of AFs per + * station during testing was 2 */ +#define MAX_EON_CNT 20 /* Maximal number of entries in the EON table (for storing + * information about other radio stations, broadcasted by the current station). + * This value is not defined by the standard, but based on observation + * of real-world RDS-TMC streams. EON doesn't seem to be a widely used feature + * and the maximum number of EON encountered during testing was 8 */ + +/* Define Constants for the possible types of RDS information + * used to address the relevant bit in the valid_fields bitmask */ +#define V4L2_RDS_PI 0x01 /* Program Identification */ +#define V4L2_RDS_PTY 0x02 /* Program Type */ +#define V4L2_RDS_TP 0x04 /* Traffic Program */ +#define V4L2_RDS_PS 0x08 /* Program Service Name */ +#define V4L2_RDS_TA 0x10 /* Traffic Announcement */ +#define V4L2_RDS_DI 0x20 /* Decoder Information */ +#define V4L2_RDS_MS 0x40 /* Music / Speech flag */ +#define V4L2_RDS_PTYN 0x80 /* Program Type Name */ +#define V4L2_RDS_RT 0x100 /* Radio-Text */ +#define V4L2_RDS_TIME 0x200 /* Date and Time information */ +#define V4L2_RDS_TMC 0x400 /* TMC availability */ +#define V4L2_RDS_AF 0x800 /* AF (alternative freq) available */ +#define V4L2_RDS_ECC 0x1000 /* Extended County Code */ +#define V4L2_RDS_LC 0x2000 /* Language Code */ +#define V4L2_RDS_TMC_SG 0x4000 /* RDS-TMC single group */ +#define V4L2_RDS_TMC_MG 0x8000 /* RDS-TMC multi group */ +#define V4L2_RDS_TMC_SYS 0x10000 /* RDS-TMC system information */ +#define V4L2_RDS_EON 0x20000 /* Enhanced Other Network Info */ +#define V4L2_RDS_LSF 0x40000 /* Linkage information */ +#define V4L2_RDS_TMC_TUNING 0x80000 /* RDS-TMC tuning information */ + +/* Define Constants for the state of the RDS decoding process + * used to address the relevant bit in the decode_information bitmask */ +#define V4L2_RDS_GROUP_NEW 0x01 /* New group received */ +#define V4L2_RDS_ODA 0x02 /* Open Data Group announced */ + +/* Decoder Information (DI) codes + * used to decode the DI information according to the RDS standard */ +#define V4L2_RDS_FLAG_STEREO 0x01 +#define V4L2_RDS_FLAG_ARTIFICIAL_HEAD 0x02 +#define V4L2_RDS_FLAG_COMPRESSED 0x04 +#define V4L2_RDS_FLAG_DYNAMIC_PTY 0x08 + +/* TMC related codes + * used to extract TMC fields from RDS-TMC groups + * see ISO 14819-1:2003, Figure 2 - RDS-TMC single-grp full message structure */ +#define V4L2_TMC_TUNING_INFO 0x10 /* Bit 4 indicates Tuning Info / User msg */ +#define V4L2_TMC_SINGLE_GROUP 0x08 /* Bit 3 indicates Single / Multi-group msg */ + +/* struct to encapsulate one complete RDS group */ +/* This structure is used internally to store data until a complete RDS + * group was received and group id dependent decoding can be done. + * It is also used to provide external access to uninterpreted RDS groups + * when manual decoding is required (e.g. special ODA types) */ +struct v4l2_rds_group { + uint16_t pi; /* Program Identification */ + char group_version; /* group version ('A' / 'B') */ + uint8_t group_id; /* group number (0..16) */ + + /* uninterpreted data blocks for decoding (e.g. ODA) */ + uint8_t data_b_lsb; + uint8_t data_c_msb; + uint8_t data_c_lsb; + uint8_t data_d_msb; + uint8_t data_d_lsb; +}; + +/* struct to encapsulate some statistical information about the decoding process */ +struct v4l2_rds_statistics { + uint32_t block_cnt; /* total amount of received blocks */ + uint32_t group_cnt; /* total amount of successfully + * decoded groups */ + uint32_t block_error_cnt; /* blocks that were marked as erroneous + * and had to be dropped */ + uint32_t group_error_cnt; /* group decoding processes that had to be + * aborted because of erroneous blocks + * or wrong order of blocks */ + uint32_t block_corrected_cnt; /* blocks that contained 1-bit errors + * which were corrected */ + uint32_t group_type_cnt[16]; /* number of occurrence for each + * defined RDS group */ +}; + +/* struct to encapsulate the definition of one ODA (Open Data Application) type */ +struct v4l2_rds_oda { + uint8_t group_id; /* RDS group used to broadcast this ODA */ + char group_version; /* group version (A / B) for this ODA */ + uint16_t aid; /* Application Identification for this ODA, + * AIDs are centrally administered by the + * RDS Registration Office (rds.org.uk) */ +}; + +/* struct to encapsulate an array of all defined ODA types for a channel */ +/* This structure will grow with ODA announcements broadcasted in type 3A + * groups, that were verified not to be no duplicates or redefinitions */ +struct v4l2_rds_oda_set { + uint8_t size; /* number of ODAs defined by this channel */ + struct v4l2_rds_oda oda[MAX_ODA_CNT]; +}; + +/* struct to encapsulate an array of Alternative Frequencies for a channel */ +/* Every channel can send out AFs for his program. The number of AFs that + * will be broadcasted is announced by the channel */ +struct v4l2_rds_af_set { + uint8_t size; /* size of the set (might be smaller + * than the announced size) */ + uint8_t announced_af; /* number of announced AF */ + uint32_t af[MAX_AF_CNT]; /* AFs defined in Hz */ +}; + +/* struct to encapsulate one entry in the EON table (Enhanced Other Network) */ +struct v4l2_rds_eon { + uint32_t valid_fields; + uint16_t pi; + uint8_t ps[9]; + uint8_t pty; + bool ta; + bool tp; + uint16_t lsf; /* Linkage Set Number */ + struct v4l2_rds_af_set af; +}; + +/* struct to encapsulate a table of EON information */ +struct v4l2_rds_eon_set { + uint8_t size; /* size of the table */ + uint8_t index; /* current position in the table */ + struct v4l2_rds_eon eon[MAX_EON_CNT]; /* Information about other + * radio channels */ +}; + +/* struct to encapsulate alternative frequencies (AFs) for RDS-TMC stations. + * AFs listed in af[] can be used unconditionally. + * AFs listed in mapped_af[n] should only be used if the current + * tuner frequency matches the value in mapped_af_tuning[n] */ +struct v4l2_tmc_alt_freq { + uint8_t af_size; /* number of known AFs */ + uint8_t af_index; + uint8_t mapped_af_size; /* number of mapped AFs */ + uint8_t mapped_af_index; + uint32_t af[MAX_TMC_AF_CNT]; /* AFs defined in Hz */ + uint32_t mapped_af[MAX_TMC_AF_CNT]; /* mapped AFs defined in Hz */ + uint32_t mapped_af_tuning[MAX_TMC_AF_CNT]; /* mapped AFs defined in Hz */ +}; + +/* struct to encapsulate information about stations carrying RDS-TMC services */ +struct v4l2_tmc_station { + uint16_t pi; + uint8_t ltn; /* database-ID of ON */ + uint8_t msg; /* msg parameters of ON */ + uint8_t sid; /* service-ID of ON */ + struct v4l2_tmc_alt_freq afi; +}; + +/* struct to encapsulate tuning information for TMC */ +struct v4l2_tmc_tuning { + uint8_t station_cnt; /* number of announced alternative stations */ + uint8_t index; + struct v4l2_tmc_station station[MAX_TMC_ALT_STATIONS]; /* information + * about other stations carrying the same RDS-TMC service */ +}; + +/* struct to encapsulate an additional data field in a TMC message */ +struct v4l2_tmc_additional { + uint8_t label; + uint16_t data; +}; + +/* struct to encapsulate an arbitrary number of additional data fields + * belonging to one TMC message */ +struct v4l2_tmc_additional_set { + uint8_t size; + struct v4l2_tmc_additional fields[MAX_TMC_ADDITIONAL]; +}; + +/* struct to encapsulate a decoded TMC message with optional additional + * data field (in case of a multi-group TMC message) */ +struct v4l2_rds_tmc_msg { + uint8_t length; /* length of multi-group message (0..4) */ + uint8_t sid; /* service identifier at time of reception */ + uint8_t extent; + uint8_t dp; /* duration and persistence */ + uint16_t event; /* TMC event code */ + uint16_t location; /* TMC event location */ + bool follow_diversion; /* indicates if the driver is adviced to + * follow the diversion */ + bool neg_direction; /* indicates negative / positive direction */ + + /* decoded additional information (only available in multi-group + * messages) */ + struct v4l2_tmc_additional_set additional; +}; + +/* struct to encapsulate all TMC related information, including TMC System + * Information, TMC Tuning information and a buffer for the last decoded + * TMC messages */ +struct v4l2_rds_tmc { + uint8_t ltn; /* location_table_number */ + bool afi; /* alternative frequency indicator */ + bool enhanced_mode; /* mode of transmission, + * if false -> basic => gaps between tmc groups + * gap defines timing behavior + * if true -> enhanced => t_a, t_w and t_d + * define timing behavior of tmc groups */ + uint8_t mgs; /* message geographical scope */ + uint8_t sid; /* service identifier (unique ID on national level) */ + uint8_t gap; /* Gap parameters */ + uint8_t t_a; /* activity time (only if mode = enhanced) */ + uint8_t t_w; /* window time (only if mode = enhanced */ + uint8_t t_d; /* delay time (only if mode = enhanced */ + uint8_t spn[9]; /* service provider name */ + struct v4l2_rds_tmc_msg tmc_msg; + + /* tuning information for alternative service providers */ + struct v4l2_tmc_tuning tuning; +}; + +/* struct to encapsulate state and RDS information for current decoding process */ +/* This is the structure that will be used by external applications, to + * communicate with the library and get access to RDS data */ +struct v4l2_rds { + /** state information **/ + uint32_t decode_information; /* state of decoding process */ + uint32_t valid_fields; /* currently valid info fields + * of this structure */ + + /** RDS info fields **/ + bool is_rbds; /* use RBDS standard version of LUTs */ + uint16_t pi; /* Program Identification */ + uint8_t ps[9]; /* Program Service Name, UTF-8 encoding, + * '\0' terminated */ + uint8_t pty; /* Program Type */ + uint8_t ptyn[9]; /* Program Type Name, UTF-8 encoding, + * '\0' terminated */ + bool ptyn_ab_flag; /* PTYN A/B flag (toggled), to signal + * change of PTYN */ + uint8_t rt_length; /* length of RT string */ + uint8_t rt[65]; /* Radio-Text string, UTF-8 encoding, + * '\0' terminated */ + bool rt_ab_flag; /* RT A/B flag (toggled), to signal + * transmission of new RT */ + bool ta; /* Traffic Announcement */ + bool tp; /* Traffic Program */ + bool ms; /* Music / Speech flag */ + uint8_t di; /* Decoder Information */ + uint8_t ecc; /* Extended Country Code */ + uint8_t lc; /* Language Code */ + time_t time; /* local time and date of transmission */ + + struct v4l2_rds_statistics rds_statistics; + struct v4l2_rds_oda_set rds_oda; /* Open Data Services */ + struct v4l2_rds_af_set rds_af; /* Alternative Frequencies */ + struct v4l2_rds_eon_set rds_eon; /* EON information */ + struct v4l2_rds_tmc tmc; /* TMC information */ +}; + +/* v4l2_rds_init() - initializes a new decoding process + * @is_rbds: defines which standard is used: true=RBDS, false=RDS + * + * initialize a new instance of the RDS-decoding struct and return + * a handle containing state and RDS information, used to interact + * with the library functions */ +LIBV4L_PUBLIC struct v4l2_rds *v4l2_rds_create(bool is_rbds); + +/* frees all memory allocated for the RDS-decoding struct */ +LIBV4L_PUBLIC void v4l2_rds_destroy(struct v4l2_rds *handle); + +/* resets the RDS information in the handle to initial values + * e.g. can be used when radio channel is changed + * @reset_statistics: true = set all statistic values to 0, false = keep them untouched */ +LIBV4L_PUBLIC void v4l2_rds_reset(struct v4l2_rds *handle, bool reset_statistics); + +/* adds a raw RDS block to decode it into RDS groups + * @return: bitmask with with updated fields set to 1 + * @rds_data: 3 bytes of raw RDS data, obtained by calling read() + * on RDS capable V4L2 devices */ +LIBV4L_PUBLIC uint32_t v4l2_rds_add(struct v4l2_rds *handle, struct v4l2_rds_data *rds_data); + +/* + * group of functions to translate numerical RDS data into strings + * + * return program description string defined in the RDS/RBDS Standard + * ! return value depends on selected Standard !*/ +LIBV4L_PUBLIC const char *v4l2_rds_get_pty_str(const struct v4l2_rds *handle); +LIBV4L_PUBLIC const char *v4l2_rds_get_language_str(const struct v4l2_rds *handle); +LIBV4L_PUBLIC const char *v4l2_rds_get_country_str(const struct v4l2_rds *handle); +LIBV4L_PUBLIC const char *v4l2_rds_get_coverage_str(const struct v4l2_rds *handle); + +/* returns a pointer to the last decoded RDS group, in order to give raw + * access to RDS data if it is required (e.g. ODA decoding) */ +LIBV4L_PUBLIC const struct v4l2_rds_group *v4l2_rds_get_group + (const struct v4l2_rds *handle); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/libv4lconvert.h b/include/libv4lconvert.h new file mode 100644 index 0000000..f41761b --- /dev/null +++ b/include/libv4lconvert.h @@ -0,0 +1,156 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA +*/ + +#ifndef __LIBV4LCONVERT_H +#define __LIBV4LCONVERT_H + +/* These headers are not needed by us, but by linux/videodev2.h, + which is broken on some systems and doesn't include them itself :( */ + +#ifdef linux +#include +#include +#include +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#include +#include +#endif + +/* end broken header workaround includes */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if HAVE_VISIBILITY +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +struct libv4l_dev_ops; +struct v4lconvert_data; + +LIBV4L_PUBLIC const struct libv4l_dev_ops *v4lconvert_get_default_dev_ops(void); + +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create(int fd); +LIBV4L_PUBLIC struct v4lconvert_data *v4lconvert_create_with_dev_ops(int fd, + void *dev_ops_priv, const struct libv4l_dev_ops *dev_ops); +LIBV4L_PUBLIC void v4lconvert_destroy(struct v4lconvert_data *data); + +/* When doing flipping / rotating / video-processing, only supported + destination formats can be used (as flipping / rotating / video-processing + is not supported on other formats). This function can be used to query + if that is the case. */ +LIBV4L_PUBLIC int v4lconvert_supported_dst_fmt_only( + struct v4lconvert_data *data); + +/* With regards to dest_fmt just like VIDIOC_TRY_FMT, except that the try + format will succeed and return the requested V4L2_PIX_FMT_foo in dest_fmt if + the cam has a format from which v4lconvert can convert to dest_fmt. + The real format to which the cam should be set is returned through src_fmt + when not NULL. + Note that just like the real VIDIOC_TRY_FMT this function will change the + dest_fmt when not supported. This includes changing it to a supported + destination format when trying a native format of the camera and + v4lconvert_supported_dst_fmt_only() returns true. */ +LIBV4L_PUBLIC int v4lconvert_try_format(struct v4lconvert_data *data, + struct v4l2_format *dest_fmt, /* in / out */ + struct v4l2_format *src_fmt); /* out */ + +/* Like VIDIOC_ENUM_FMT, but the emulated formats are added at the end of the + list, except if flipping / processing is active for the device, then only + supported destination formats are listed */ +LIBV4L_PUBLIC int v4lconvert_enum_fmt(struct v4lconvert_data *data, + struct v4l2_fmtdesc *fmt); + +/* Is conversion necessary or can the app use the data directly? */ +LIBV4L_PUBLIC int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt); /* in */ + +/* This function does the following conversions: + - format conversion + - cropping + if enabled: + - processing (auto whitebalance, auto gain, gamma correction) + - horizontal/vertical flipping + - 90 degree (clockwise) rotation + + NOTE: the last 3 steps are enabled/disabled depending on + - the internal device list + - the state of the (software emulated) image controls + + Therefore this function should + - not be used when getting the frames from libv4l + - be called only once per frame + Otherwise this may result in unintended double conversions ! + + Returns the amount of bytes written to dest and -1 on error */ +LIBV4L_PUBLIC int v4lconvert_convert(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt, /* in */ + unsigned char *src, int src_size, unsigned char *dest, int dest_size); + +/* get a string describing the last error */ +LIBV4L_PUBLIC const char *v4lconvert_get_error_message(struct v4lconvert_data *data); + +/* Just like VIDIOC_ENUM_FRAMESIZE, except that the framesizes of emulated + formats can be enumerated as well. */ +LIBV4L_PUBLIC int v4lconvert_enum_framesizes(struct v4lconvert_data *data, + struct v4l2_frmsizeenum *frmsize); + +/* Just like VIDIOC_ENUM_FRAMEINTERVALS, except that the intervals of emulated + formats can be enumerated as well. */ +LIBV4L_PUBLIC int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, + struct v4l2_frmivalenum *frmival); + +/* Pass calls to query, get and set video controls to the libv4lcontrol class */ +LIBV4L_PUBLIC int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_g_ext_ctrls(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_try_ext_ctrls(struct v4lconvert_data *data, + void *arg); +LIBV4L_PUBLIC int v4lconvert_vidioc_s_ext_ctrls(struct v4lconvert_data *data, + void *arg); + +/* Is the passed in pixelformat supported as destination format? */ +LIBV4L_PUBLIC int v4lconvert_supported_dst_format(unsigned int pixelformat); + +/* Get/set the no fps libv4lconvert uses to decide if a compressed format + must be used as src fmt to stay within the bus bandwidth */ +LIBV4L_PUBLIC int v4lconvert_get_fps(struct v4lconvert_data *data); +LIBV4L_PUBLIC void v4lconvert_set_fps(struct v4lconvert_data *data, int fps); + +/* Fixup bytesperline and sizeimage for supported destination formats */ +LIBV4L_PUBLIC void v4lconvert_fixup_fmt(struct v4l2_format *fmt); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/libv4l2/Makefile b/libv4l2/Makefile new file mode 100644 index 0000000..fca7e34 --- /dev/null +++ b/libv4l2/Makefile @@ -0,0 +1,61 @@ +############################################################################### +# +# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA Corporation is strictly prohibited. +# +############################################################################### + +SO_NAME := libnvv4l2.so + +SRCS := libv4l2.c log.c v4l2convert.c v4l2-plugin.c + +INCLUDES += -I./ -I../include + +OBJS := $(SRCS:.c=.o) + +CFLAGS := -fPIC + +MACHINE = $(shell uname -m) + +ifeq ($(MACHINE),x86_64) + CFLAGS += -DLIBV4L2_PLUGIN_DIR_PATH_X86 + DEST_DIR ?= /opt/nvidia/deepstream/deepstream-4.0/lib/ + SYM_LINK_DIR = $(DEST_DIR) +else + DEST_DIR ?= /usr/lib/$(MACHINE)-linux-gnu/tegra + SYM_LINK_DIR = $(shell realpath $(DEST_DIR)/..) +endif + +LDFLAGS := -L$(DEST_DIR) -Wl,-soname,libv4l2.so.0 + +LIBS := -lv4lconvert -ldl + +all: $(SO_NAME) + +%.o: %.c + $(CC) -c $< $(CFLAGS) $(INCLUDES) -o $@ + +$(SO_NAME): $(OBJS) + $(CC) -shared -o $(SO_NAME) $(OBJS) $(LIBS) $(LDFLAGS) + +.PHONY: install +install: $(SO_NAME) + cp -vp $(SO_NAME) $(DEST_DIR) + if [ "$(MACHINE)" = "aarch64" ]; then \ + ln -sf tegra/$(SO_NAME) $(SYM_LINK_DIR)/libv4l2.so.0.0.999999 ; \ + else \ + ln -sf $(SO_NAME) $(SYM_LINK_DIR)/libv4l2.so.0.0.999999 ; \ + fi + ln -sf libv4l2.so.0.0.999999 \ + $(SYM_LINK_DIR)/libv4l2.so + ln -sf libv4l2.so.0.0.999999 \ + ${SYM_LINK_DIR}/libv4l2.so.0 + +.PHONY: clean +clean: + rm -rf $(OBJS) $(SO_NAME) diff --git a/libv4l2/Makefile.am b/libv4l2/Makefile.am new file mode 100644 index 0000000..b6f4d3b --- /dev/null +++ b/libv4l2/Makefile.am @@ -0,0 +1,28 @@ +if WITH_LIBV4L +lib_LTLIBRARIES = libv4l2.la +include_HEADERS = ../include/libv4l2.h ../include/libv4l-plugin.h +pkgconfig_DATA = libv4l2.pc +LIBV4L2_VERSION = -version-info 0 +if WITH_V4L_WRAPPERS +libv4l2priv_LTLIBRARIES = v4l2convert.la + +install-exec-hook: + $(MKDIR_P) $(DESTDIR)/$(libdir) + (cd $(DESTDIR)/$(libdir) && rm -f v4l2convert.so && $(LN_S) $(libv4l2subdir)/v4l2convert.so v4l2convert.so) + +endif +else +noinst_LTLIBRARIES = libv4l2.la +endif + +libv4l2_la_SOURCES = libv4l2.c v4l2-plugin.c log.c libv4l2-priv.h +libv4l2_la_CPPFLAGS = $(CFLAG_VISIBILITY) $(ENFORCE_LIBV4L_STATIC) +libv4l2_la_LDFLAGS = $(LIBV4L2_VERSION) -lpthread $(DLOPEN_LIBS) $(ENFORCE_LIBV4L_STATIC) +libv4l2_la_LIBADD = ../libv4lconvert/libv4lconvert.la + +v4l2convert_la_SOURCES = v4l2convert.c +v4l2convert_la_LIBADD = libv4l2.la +v4l2convert_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +v4l2convert_la_LIBTOOLFLAGS = --tag=disable-static + +EXTRA_DIST = Android.mk v4l2-plugin-android.c diff --git a/libv4l2/Makefile.dGPU b/libv4l2/Makefile.dGPU new file mode 100644 index 0000000..97c88ef --- /dev/null +++ b/libv4l2/Makefile.dGPU @@ -0,0 +1,36 @@ +############################################################################### +# +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA Corporation is strictly prohibited. +# +############################################################################### +CC:=gcc +TARGET_NAME:= libnvv4l2.so + +SRCS := libv4l2.c log.c v4l2convert.c v4l2-plugin.c + +INC_PATHS := ./ ../include ../../include + +CFLAGS := -fPIC +CFLAGS += -DLIBV4L2_PLUGIN_DIR_PATH_X86 + +IGNORE_DS_PACKAGE_NAMING:=1 + +LDFLAGS:= -shared +LIBS:= -lv4lconvert -ldl -Wl,-soname,libv4l2.so.0 + +#IS_V4L2_LIB:=1 +PACKAGE_BINARY_IN_DS:=1 + +BUILD_DIR:=../../../../deepstream/sdk/build/libs/libv4l/ + +include ../../../../deepstream/sdk/Rules.mk + +install:: + ln -sf $(INSTALL_DIR)/$(TARGET_NAME) /usr/lib/x86_64-linux-gnu/libv4l2.so.0.0.99999 + ldconfig diff --git a/libv4l2/libv4l2-priv.h b/libv4l2/libv4l2-priv.h new file mode 100644 index 0000000..717e23f --- /dev/null +++ b/libv4l2/libv4l2-priv.h @@ -0,0 +1,118 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifndef __LIBV4L2_PRIV_H +#define __LIBV4L2_PRIV_H + +#include +#include +#include /* includes videodev2.h for us */ + +#include "../libv4lconvert/libv4lsyscall-priv.h" + +#define V4L2_MAX_DEVICES 1024 +/* Warning when making this larger the frame_queued and frame_mapped members of + the v4l2_dev_info struct can no longer be a bitfield, so the code needs to + be adjusted! */ +#define V4L2_MAX_NO_FRAMES 32 +#define V4L2_DEFAULT_NREADBUFFERS 4 +#define V4L2_IGNORE_FIRST_FRAME_ERRORS 3 +#define V4L2_DEFAULT_FPS 30 + +#define V4L2_LOG_ERR(...) \ + do { \ + if (v4l2_log_file) { \ + fprintf(v4l2_log_file, "libv4l2: error " __VA_ARGS__); \ + fflush(v4l2_log_file); \ + } else \ + fprintf(stderr, "libv4l2: error " __VA_ARGS__); \ + } while (0) + +#define V4L2_PERROR(format, ...) \ + do { \ + if (errno == ENODEV) { \ + devices[index].gone = 1;\ + break; \ + } \ + V4L2_LOG_ERR(format ": %s\n", ##__VA_ARGS__, strerror(errno)); \ + } while (0) + +#define V4L2_LOG_WARN(...) \ + do { \ + if (v4l2_log_file) { \ + fprintf(v4l2_log_file, "libv4l2: warning " __VA_ARGS__); \ + fflush(v4l2_log_file); \ + } else \ + fprintf(stderr, "libv4l2: warning " __VA_ARGS__); \ + } while (0) + +#define V4L2_LOG(...) \ + do { \ + if (v4l2_log_file) { \ + fprintf(v4l2_log_file, "libv4l2: " __VA_ARGS__); \ + fflush(v4l2_log_file); \ + } \ + } while (0) + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +struct v4l2_dev_info { + int fd; + int flags; + int open_count; + int gone; /* Set to 1 when a device is detached (ENODEV encountered) */ + long page_size; + /* actual format of the cam */ + struct v4l2_format src_fmt; + /* fmt as seen by the application (iow after conversion) */ + struct v4l2_format dest_fmt; + pthread_mutex_t stream_lock; + unsigned int no_frames; + unsigned int nreadbuffers; + int fps; + int first_frame; + struct v4lconvert_data *convert; + unsigned char *convert_mmap_buf; + size_t convert_mmap_buf_size; + size_t convert_mmap_frame_size; + /* Frame bookkeeping is only done when in read or mmap-conversion mode */ + unsigned char *frame_pointers[V4L2_MAX_NO_FRAMES]; + int frame_sizes[V4L2_MAX_NO_FRAMES]; + int frame_queued; /* 1 status bit per frame */ + int frame_info_generation; + /* mapping tracking of our fake (converting mmap) frame buffers */ + unsigned char frame_map_count[V4L2_MAX_NO_FRAMES]; + /* buffer when doing conversion and using read() for read() */ + int readbuf_size; + unsigned char *readbuf; + /* plugin info */ + void *plugin_library; + void *dev_ops_priv; + const struct libv4l_dev_ops *dev_ops; +}; + +/* From v4l2-plugin.c */ +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret, + const struct libv4l_dev_ops **dev_ops_ret); +void v4l2_plugin_cleanup(void *plugin_lib, void *plugin_priv, + const struct libv4l_dev_ops *dev_ops); + +/* From log.c */ +extern const char *v4l2_ioctls[]; +void v4l2_log_ioctl(unsigned long int request, void *arg, int result); + +#endif diff --git a/libv4l2/libv4l2.c b/libv4l2/libv4l2.c new file mode 100644 index 0000000..f90646a --- /dev/null +++ b/libv4l2/libv4l2.c @@ -0,0 +1,1781 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +/* MAKING CHANGES TO THIS FILE?? READ THIS FIRST!!! + + This file implements libv4l2, which offers v4l2_ prefixed versions of + open/close/etc. The API is 100% the same as directly opening /dev/videoX + using regular open/close/etc, the big difference is that format conversion + is done if necessary when capturing. That is if you (try to) set a capture + format which is not supported by the cam, but is supported by libv4lconvert, + then the try_fmt / set_fmt will succeed as if the cam supports the format + and on dqbuf / read the data will be converted for you and returned in + the request format. + + Important note to people making changes to this file: All functions + (v4l2_close, v4l2_ioctl, etc.) are designed to function as their regular + counterpart when they get passed a fd that is not "registered" by libv4l2, + there are 2 reasons for this: + 1) This allows us to get completely out of the way when dealing with non + capture devices. + 2) libv4l2 is the base of the v4l2convert.so wrapper lib, which is a .so + which can be LD_PRELOAD-ed and the overrules the libc's open/close/etc, + and when opening /dev/videoX or /dev/v4l/ calls v4l2_open. Because we + behave as the regular counterpart when the fd is not known (instead of say + throwing an error), v4l2convert.so can simply call the v4l2_ prefixed + function for all wrapped functions (except for v4l2_open which will fail + when not called on a v4l2 device). This way the wrapper does not have to + keep track of which fd's are being handled by libv4l2, as libv4l2 already + keeps track of this itself. + + This also means that libv4l2 may not use any of the regular functions + it mimics, as for example open could be a symbol in v4l2convert.so, which + in turn will call v4l2_open, so therefore v4l2_open (for example) may not + use the regular open()! + + Another important note: libv4l2 does conversion for capture usage only, if + any calls are made which are passed a v4l2_buffer or v4l2_format with a + v4l2_buf_type which is different from V4L2_BUF_TYPE_VIDEO_CAPTURE, then + the v4l2_ methods behave exactly the same as their regular counterparts. + When modifications are made, one should be careful that this behavior is + preserved. + */ +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libv4l2.h" +#include "libv4l2-priv.h" +#include "libv4l-plugin.h" + +/* Note these flags are stored together with the flags passed to v4l2_fd_open() + in v4l2_dev_info's flags member, so care should be taken that the do not + use the same bits! */ +#define V4L2_STREAMON 0x0100 +#define V4L2_BUFFERS_REQUESTED_BY_READ 0x0200 +#define V4L2_STREAM_CONTROLLED_BY_READ 0x0400 +#define V4L2_SUPPORTS_READ 0x0800 +#define V4L2_STREAM_TOUCHED 0x1000 +#define V4L2_USE_READ_FOR_READ 0x2000 +#define V4L2_SUPPORTS_TIMEPERFRAME 0x4000 + +#define V4L2_MMAP_OFFSET_MAGIC 0xABCDEF00u + +static void v4l2_adjust_src_fmt_to_fps(int index, int fps); +static void v4l2_set_src_and_dest_format(int index, + struct v4l2_format *src_fmt, struct v4l2_format *dest_fmt); + +static pthread_mutex_t v4l2_open_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct v4l2_dev_info devices[V4L2_MAX_DEVICES] = { + [0 ... V4L2_MAX_DEVICES-1].fd = -1 +}; + +static int devices_used; + +static int v4l2_ensure_convert_mmap_buf(int index) +{ + if (devices[index].convert_mmap_buf != MAP_FAILED) { + return 0; + } + + devices[index].convert_mmap_buf_size = + devices[index].convert_mmap_frame_size * devices[index].no_frames; + + devices[index].convert_mmap_buf = (void *)SYS_MMAP(NULL, + devices[index].convert_mmap_buf_size, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, + -1, 0); + + if (devices[index].convert_mmap_buf == MAP_FAILED) { + devices[index].convert_mmap_buf_size = 0; + + int saved_err = errno; + V4L2_LOG_ERR("allocating conversion buffer\n"); + errno = saved_err; + return -1; + } + + return 0; +} + +static int v4l2_request_read_buffers(int index) +{ + int result; + struct v4l2_requestbuffers req; + + /* Note we re-request the buffers if they are already requested as the format + and thus the needed buffer size may have changed. */ + req.count = (devices[index].no_frames) ? devices[index].no_frames : + devices[index].nreadbuffers; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_REQBUFS, &req); + if (result < 0) { + int saved_err = errno; + + V4L2_LOG("warning reqbuf (%u) failed: %s\n", req.count, strerror(errno)); + errno = saved_err; + return result; + } + + if (!devices[index].no_frames && req.count) + devices[index].flags |= V4L2_BUFFERS_REQUESTED_BY_READ; + + devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); + return 0; +} + +static void v4l2_unrequest_read_buffers(int index) +{ + struct v4l2_requestbuffers req; + + if (!(devices[index].flags & V4L2_BUFFERS_REQUESTED_BY_READ) || + devices[index].no_frames == 0) + return; + + /* (Un)Request buffers, note not all driver support this, and those + who do not support it don't need it. */ + req.count = 0; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_REQBUFS, &req) < 0) + return; + + devices[index].no_frames = MIN(req.count, V4L2_MAX_NO_FRAMES); + if (devices[index].no_frames == 0) + devices[index].flags &= ~V4L2_BUFFERS_REQUESTED_BY_READ; +} + +static int v4l2_map_buffers(int index) +{ + int result = 0; + unsigned int i; + struct v4l2_buffer buf; + + for (i = 0; i < devices[index].no_frames; i++) { + if (devices[index].frame_pointers[i] != MAP_FAILED) + continue; + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + buf.reserved = buf.reserved2 = 0; + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_QUERYBUF, &buf); + if (result) { + int saved_err = errno; + + V4L2_PERROR("querying buffer %u", i); + errno = saved_err; + break; + } + + devices[index].frame_pointers[i] = (void *)SYS_MMAP(NULL, + (size_t)buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, devices[index].fd, + buf.m.offset); + if (devices[index].frame_pointers[i] == MAP_FAILED) { + int saved_err = errno; + + V4L2_PERROR("mmapping buffer %u", i); + errno = saved_err; + result = -1; + break; + } + V4L2_LOG("mapped buffer %u at %p\n", i, + devices[index].frame_pointers[i]); + + devices[index].frame_sizes[i] = buf.length; + } + + return result; +} + +static void v4l2_unmap_buffers(int index) +{ + unsigned int i; + + /* unmap the buffers */ + for (i = 0; i < devices[index].no_frames; i++) { + if (devices[index].frame_pointers[i] != MAP_FAILED) { + SYS_MUNMAP(devices[index].frame_pointers[i], + devices[index].frame_sizes[i]); + devices[index].frame_pointers[i] = MAP_FAILED; + V4L2_LOG("unmapped buffer %u\n", i); + } + } +} + +static int v4l2_streamon(int index) +{ + int result; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (!(devices[index].flags & V4L2_STREAMON)) { + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_STREAMON, &type); + if (result) { + int saved_err = errno; + + V4L2_PERROR("turning on stream"); + errno = saved_err; + return result; + } + devices[index].flags |= V4L2_STREAMON; + devices[index].first_frame = V4L2_IGNORE_FIRST_FRAME_ERRORS; + } + + return 0; +} + +static int v4l2_streamoff(int index) +{ + int result; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (devices[index].flags & V4L2_STREAMON) { + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_STREAMOFF, &type); + if (result) { + int saved_err = errno; + + V4L2_PERROR("turning off stream"); + errno = saved_err; + return result; + } + devices[index].flags &= ~V4L2_STREAMON; + + /* Stream off also dequeues all our buffers! */ + devices[index].frame_queued = 0; + } + + return 0; +} + +static int v4l2_queue_read_buffer(int index, int buffer_index) +{ + int result; + struct v4l2_buffer buf; + + if (devices[index].frame_queued & (1 << buffer_index)) + return 0; + + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = buffer_index; + result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_QBUF, &buf); + if (result) { + int saved_err = errno; + + V4L2_PERROR("queuing buf %d", buffer_index); + errno = saved_err; + return result; + } + + devices[index].frame_queued |= 1 << buffer_index; + return 0; +} + +static int v4l2_dequeue_and_convert(int index, struct v4l2_buffer *buf, + unsigned char *dest, int dest_size) +{ + const int max_tries = V4L2_IGNORE_FIRST_FRAME_ERRORS + 1; + int result, tries = max_tries, frame_info_gen; + + /* Make sure we have the real v4l2 buffers mapped */ + result = v4l2_map_buffers(index); + if (result) + return result; + + do { + frame_info_gen = devices[index].frame_info_generation; + pthread_mutex_unlock(&devices[index].stream_lock); + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_DQBUF, buf); + pthread_mutex_lock(&devices[index].stream_lock); + if (result) { + if (errno != EAGAIN) { + int saved_err = errno; + + V4L2_PERROR("dequeuing buf"); + errno = saved_err; + } + return result; + } + + devices[index].frame_queued &= ~(1 << buf->index); + + if (frame_info_gen != devices[index].frame_info_generation) { + errno = -EINVAL; + return -1; + } + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].frame_pointers[buf->index], + buf->bytesused, dest ? dest : (devices[index].convert_mmap_buf + + buf->index * devices[index].convert_mmap_frame_size), + dest_size); + + if (devices[index].first_frame) { + /* Always treat convert errors as EAGAIN during the first few frames, as + some cams produce bad frames at the start of the stream + (hsync and vsync still syncing ??). */ + if (result < 0) + errno = EAGAIN; + devices[index].first_frame--; + } + + if (result < 0) { + int saved_err = errno; + + if (errno == EAGAIN || errno == EPIPE) + V4L2_LOG("warning error while converting frame data: %s", + v4lconvert_get_error_message(devices[index].convert)); + else + V4L2_LOG_ERR("converting / decoding frame data: %s", + v4lconvert_get_error_message(devices[index].convert)); + + /* + * If this is the last try, and the frame is short + * we will return the (short) buffer to the caller, + * so we must not re-queue it then! + */ + if (!(tries == 1 && errno == EPIPE)) + v4l2_queue_read_buffer(index, buf->index); + errno = saved_err; + } + tries--; + } while (result < 0 && (errno == EAGAIN || errno == EPIPE) && tries); + + if (result < 0 && errno == EAGAIN) { + V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s", + max_tries, v4lconvert_get_error_message(devices[index].convert)); + errno = EIO; + } + + if (result < 0 && errno == EPIPE) { + V4L2_LOG("got %d consecutive short frame errors, " + "returning short frame", max_tries); + result = devices[index].dest_fmt.fmt.pix.sizeimage; + errno = 0; + } + + return result; +} + +static int v4l2_read_and_convert(int index, unsigned char *dest, int dest_size) +{ + const int max_tries = V4L2_IGNORE_FIRST_FRAME_ERRORS + 1; + int result, buf_size, tries = max_tries; + + buf_size = devices[index].dest_fmt.fmt.pix.sizeimage; + + if (devices[index].readbuf_size < buf_size) { + unsigned char *new_buf; + + new_buf = realloc(devices[index].readbuf, buf_size); + if (!new_buf) + return -1; + + devices[index].readbuf = new_buf; + devices[index].readbuf_size = buf_size; + } + + do { + result = devices[index].dev_ops->read( + devices[index].dev_ops_priv, + devices[index].fd, devices[index].readbuf, + buf_size); + if (result <= 0) { + if (result && errno != EAGAIN) { + int saved_err = errno; + + V4L2_PERROR("reading"); + errno = saved_err; + } + return result; + } + + result = v4lconvert_convert(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt, + devices[index].readbuf, result, dest, dest_size); + + if (devices[index].first_frame) { + /* Always treat convert errors as EAGAIN during the first few frames, as + some cams produce bad frames at the start of the stream + (hsync and vsync still syncing ??). */ + if (result < 0) + errno = EAGAIN; + devices[index].first_frame--; + } + + if (result < 0) { + int saved_err = errno; + + if (errno == EAGAIN || errno == EPIPE) + V4L2_LOG("warning error while converting frame data: %s", + v4lconvert_get_error_message(devices[index].convert)); + else + V4L2_LOG_ERR("converting / decoding frame data: %s", + v4lconvert_get_error_message(devices[index].convert)); + + errno = saved_err; + } + tries--; + } while (result < 0 && (errno == EAGAIN || errno == EPIPE) && tries); + + if (result < 0 && errno == EAGAIN) { + V4L2_LOG_ERR("got %d consecutive frame decode errors, last error: %s", + max_tries, v4lconvert_get_error_message(devices[index].convert)); + errno = EIO; + } + + if (result < 0 && errno == EPIPE) { + V4L2_LOG("got %d consecutive short frame errors, " + "returning short frame", max_tries); + result = devices[index].dest_fmt.fmt.pix.sizeimage; + errno = 0; + } + + return result; +} + +static int v4l2_queue_read_buffers(int index) +{ + unsigned int i; + int last_error = EIO, queued = 0; + + for (i = 0; i < devices[index].no_frames; i++) { + /* Don't queue unmapped buffers (should never happen) */ + if (devices[index].frame_pointers[i] != MAP_FAILED) { + if (v4l2_queue_read_buffer(index, i)) { + last_error = errno; + continue; + } + queued++; + } + } + + if (!queued) { + errno = last_error; + return -1; + } + return 0; +} + +static int v4l2_activate_read_stream(int index) +{ + int result; + + if ((devices[index].flags & V4L2_STREAMON) || devices[index].frame_queued) { + errno = EBUSY; + return -1; + } + + result = v4l2_request_read_buffers(index); + if (!result) + result = v4l2_map_buffers(index); + if (!result) + result = v4l2_queue_read_buffers(index); + if (result) + return result; + + devices[index].flags |= V4L2_STREAM_CONTROLLED_BY_READ; + + return v4l2_streamon(index); +} + +static int v4l2_deactivate_read_stream(int index) +{ + int result; + + result = v4l2_streamoff(index); + if (result) + return result; + + /* No need to dequeue our buffers, streamoff does that for us */ + + v4l2_unmap_buffers(index); + + v4l2_unrequest_read_buffers(index); + + devices[index].flags &= ~V4L2_STREAM_CONTROLLED_BY_READ; + + return 0; +} + +static int v4l2_needs_conversion(int index) +{ + if (devices[index].convert == NULL) + return 0; + + return v4lconvert_needs_conversion(devices[index].convert, + &devices[index].src_fmt, &devices[index].dest_fmt); +} + +static void v4l2_set_conversion_buf_params(int index, struct v4l2_buffer *buf) +{ + if (!v4l2_needs_conversion(index)) + return; + + /* This may happen if the ioctl failed */ + if (buf->index >= devices[index].no_frames) + buf->index = 0; + + buf->m.offset = V4L2_MMAP_OFFSET_MAGIC | buf->index; + buf->length = devices[index].convert_mmap_frame_size; + if (devices[index].frame_map_count[buf->index]) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + else + buf->flags &= ~V4L2_BUF_FLAG_MAPPED; +} + +static int v4l2_buffers_mapped(int index) +{ + unsigned int i; + + if (!v4l2_needs_conversion(index)) { + /* Normal (no conversion) mode */ + struct v4l2_buffer buf; + + for (i = 0; i < devices[index].no_frames; i++) { + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + buf.reserved = buf.reserved2 = 0; + if (devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_QUERYBUF, + &buf)) { + int saved_err = errno; + + V4L2_PERROR("querying buffer %u", i); + errno = saved_err; + break; + } + if (buf.flags & V4L2_BUF_FLAG_MAPPED) + break; + } + } else { + /* Conversion mode */ + for (i = 0; i < devices[index].no_frames; i++) + if (devices[index].frame_map_count[i]) + break; + } + + if (i != devices[index].no_frames) + V4L2_LOG("v4l2_buffers_mapped(): buffers still mapped\n"); + + return i != devices[index].no_frames; +} + +static void v4l2_update_fps(int index, struct v4l2_streamparm *parm) +{ + if ((devices[index].flags & V4L2_SUPPORTS_TIMEPERFRAME) && + parm->parm.capture.timeperframe.numerator != 0) { + int fps = parm->parm.capture.timeperframe.denominator; + fps += parm->parm.capture.timeperframe.numerator - 1; + fps /= parm->parm.capture.timeperframe.numerator; + devices[index].fps = fps; + } else + devices[index].fps = 0; +} + +int v4l2_open(const char *file, int oflag, ...) +{ + int fd; + + /* original open code */ + if (oflag & O_CREAT) { + va_list ap; + mode_t mode; + + va_start(ap, oflag); + mode = va_arg(ap, PROMOTED_MODE_T); + + fd = SYS_OPEN(file, oflag, mode); + + va_end(ap); + } else { + fd = SYS_OPEN(file, oflag, 0); + } + /* end of original open code */ + + if (fd == -1) + return fd; + + if (v4l2_fd_open(fd, 0) == -1) { + int saved_err = errno; + + SYS_CLOSE(fd); + errno = saved_err; + return -1; + } + + return fd; +} + +int v4l2_fd_open(int fd, int v4l2_flags) +{ + int i, index; + char *lfname; + struct v4l2_capability cap; + struct v4l2_format fmt = { 0, }; + struct v4l2_streamparm parm = { 0, }; + struct v4lconvert_data *convert = NULL; + void *plugin_library; + void *dev_ops_priv; + const struct libv4l_dev_ops *dev_ops; + long page_size; + + v4l2_plugin_init(fd, &plugin_library, &dev_ops_priv, &dev_ops); + + /* If no log file was set by the app, see if one was specified through the + environment */ + if (!v4l2_log_file) { + lfname = getenv("LIBV4L2_LOG_FILENAME"); + if (lfname) + v4l2_log_file = fopen(lfname, "w"); + } + + /* Get page_size (for mmap emulation) */ + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) { + int saved_err = errno; + V4L2_LOG_ERR("unable to retrieve page size: %s\n", + strerror(errno)); + v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops); + errno = saved_err; + return -1; + } + + /* check that this is a v4l2 device */ + if (dev_ops->ioctl(dev_ops_priv, fd, VIDIOC_QUERYCAP, &cap)) { + int saved_err = errno; + V4L2_LOG_ERR("getting capabilities: %s\n", strerror(errno)); + v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops); + errno = saved_err; + return -1; + } + + if (cap.capabilities & V4L2_CAP_DEVICE_CAPS) + cap.capabilities = cap.device_caps; + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) || + !(cap.capabilities & (V4L2_CAP_STREAMING | V4L2_CAP_READWRITE))) + goto no_capture; + + /* Get current cam format */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (dev_ops->ioctl(dev_ops_priv, fd, VIDIOC_G_FMT, &fmt)) { + int saved_err = errno; + V4L2_LOG_ERR("getting pixformat: %s\n", strerror(errno)); + v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops); + errno = saved_err; + return -1; + } + + /* Check for frame rate setting support */ + parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (dev_ops->ioctl(dev_ops_priv, fd, VIDIOC_G_PARM, &parm)) + parm.type = 0; + + /* init libv4lconvert */ + if (!(v4l2_flags & V4L2_DISABLE_CONVERSION)) { + convert = v4lconvert_create_with_dev_ops(fd, dev_ops_priv, dev_ops); + if (!convert) { + int saved_err = errno; + v4l2_plugin_cleanup(plugin_library, dev_ops_priv, + dev_ops); + errno = saved_err; + return -1; + } + } + +no_capture: + /* So we have a v4l2 capture device, register it in our devices array */ + pthread_mutex_lock(&v4l2_open_mutex); + for (index = 0; index < V4L2_MAX_DEVICES; index++) { + if (devices[index].fd == -1) { + devices[index].fd = fd; + devices[index].plugin_library = plugin_library; + devices[index].dev_ops_priv = dev_ops_priv; + devices[index].dev_ops = dev_ops; + break; + } + } + pthread_mutex_unlock(&v4l2_open_mutex); + + if (index == V4L2_MAX_DEVICES) { + V4L2_LOG_ERR("attempting to open more then %d video devices\n", + V4L2_MAX_DEVICES); + v4l2_plugin_cleanup(plugin_library, dev_ops_priv, dev_ops); + errno = EBUSY; + return -1; + } + + devices[index].flags = v4l2_flags; + if (cap.capabilities & V4L2_CAP_READWRITE) + devices[index].flags |= V4L2_SUPPORTS_READ; + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + devices[index].flags |= V4L2_USE_READ_FOR_READ; + /* This device only supports read so the stream gets started by the + driver on the first read */ + devices[index].first_frame = V4L2_IGNORE_FIRST_FRAME_ERRORS; + } + if ((parm.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) + devices[index].flags |= V4L2_SUPPORTS_TIMEPERFRAME; + devices[index].open_count = 1; + devices[index].page_size = page_size; + devices[index].src_fmt = fmt; + devices[index].dest_fmt = fmt; + v4l2_set_src_and_dest_format(index, &devices[index].src_fmt, + &devices[index].dest_fmt); + + pthread_mutex_init(&devices[index].stream_lock, NULL); + + devices[index].no_frames = 0; + devices[index].nreadbuffers = V4L2_DEFAULT_NREADBUFFERS; + devices[index].convert = convert; + devices[index].convert_mmap_buf = MAP_FAILED; + devices[index].convert_mmap_buf_size = 0; + for (i = 0; i < V4L2_MAX_NO_FRAMES; i++) { + devices[index].frame_pointers[i] = MAP_FAILED; + devices[index].frame_map_count[i] = 0; + } + devices[index].frame_queued = 0; + devices[index].readbuf = NULL; + devices[index].readbuf_size = 0; + + if (index >= devices_used) + devices_used = index + 1; + + /* Note we always tell v4lconvert to optimize src fmt selection for + our default fps, the only exception is the app explicitly selecting + a fram erate using the S_PARM ioctl after a S_FMT */ + if (devices[index].convert) + v4lconvert_set_fps(devices[index].convert, V4L2_DEFAULT_FPS); + v4l2_update_fps(index, &parm); + + V4L2_LOG("open: %d\n", fd); + + return fd; +} + +/* Is this an fd for which we are emulating v4l1 ? */ +static int v4l2_get_index(int fd) +{ + int index; + + /* We never handle fd -1 */ + if (fd == -1) + return -1; + + for (index = 0; index < devices_used; index++) + if (devices[index].fd == fd) + break; + + if (index == devices_used) + return -1; + + return index; +} + + +int v4l2_close(int fd) +{ + int index, result; + + index = v4l2_get_index(fd); + if (index == -1) + return SYS_CLOSE(fd); + + /* Abuse stream_lock to stop 2 closes from racing and trying to free + the resources twice */ + pthread_mutex_lock(&devices[index].stream_lock); + devices[index].open_count--; + result = devices[index].open_count != 0; + pthread_mutex_unlock(&devices[index].stream_lock); + + if (result) + return 0; + + v4l2_plugin_cleanup(devices[index].plugin_library, + devices[index].dev_ops_priv, + devices[index].dev_ops); + + /* Free resources */ + v4l2_unmap_buffers(index); + if (devices[index].convert_mmap_buf != MAP_FAILED) { + if (v4l2_buffers_mapped(index)) { + if (!devices[index].gone) + V4L2_LOG_WARN("v4l2 mmap buffers still mapped on close()\n"); + } else { + SYS_MUNMAP(devices[index].convert_mmap_buf, + devices[index].convert_mmap_buf_size); + } + devices[index].convert_mmap_buf = MAP_FAILED; + devices[index].convert_mmap_buf_size = 0; + } + v4lconvert_destroy(devices[index].convert); + free(devices[index].readbuf); + devices[index].readbuf = NULL; + devices[index].readbuf_size = 0; + + /* Remove the fd from our list of managed fds before closing it, because as + soon as we've done the actual close, the fd maybe returned by an open() in + another thread and we don't want to intercept calls to this new fd. */ + devices[index].fd = -1; + + /* Since we've marked the fd as no longer used, and freed the resources, + redo the close in case it was interrupted */ + do { + result = SYS_CLOSE(fd); + } while (result == -1 && errno == EINTR); + + V4L2_LOG("close: %d\n", fd); + + return result; +} + +int v4l2_dup(int fd) +{ + int index = v4l2_get_index(fd); + + if (index == -1) + return syscall(SYS_dup, fd); + + devices[index].open_count++; + + return fd; +} + +static int v4l2_check_buffer_change_ok(int index) +{ + devices[index].frame_info_generation++; + v4l2_unmap_buffers(index); + + /* Check if the app itself still is using the stream */ + if (v4l2_buffers_mapped(index) || + (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) && + ((devices[index].flags & V4L2_STREAMON) || + devices[index].frame_queued))) { + V4L2_LOG("v4l2_check_buffer_change_ok(): stream busy\n"); + errno = EBUSY; + return -1; + } + + /* We may change from convert to non conversion mode and + v4l2_unrequest_read_buffers may change the no_frames, so free the + convert mmap buffer */ + SYS_MUNMAP(devices[index].convert_mmap_buf, + devices[index].convert_mmap_buf_size); + devices[index].convert_mmap_buf = MAP_FAILED; + devices[index].convert_mmap_buf_size = 0; + + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + V4L2_LOG("deactivating read-stream for settings change\n"); + return v4l2_deactivate_read_stream(index); + } + + return 0; +} + +static int v4l2_pix_fmt_compat(struct v4l2_format *a, struct v4l2_format *b) +{ + if (a->fmt.pix.width == b->fmt.pix.width && + a->fmt.pix.height == b->fmt.pix.height && + a->fmt.pix.pixelformat == b->fmt.pix.pixelformat && + a->fmt.pix.field == b->fmt.pix.field) + return 1; + + return 0; +} + +static int v4l2_pix_fmt_identical(struct v4l2_format *a, struct v4l2_format *b) +{ + if (v4l2_pix_fmt_compat(a, b) && + a->fmt.pix.bytesperline == b->fmt.pix.bytesperline && + a->fmt.pix.sizeimage == b->fmt.pix.sizeimage) + return 1; + + return 0; +} + +static void v4l2_set_src_and_dest_format(int index, + struct v4l2_format *src_fmt, struct v4l2_format *dest_fmt) +{ + /* + * When a user does a try_fmt with the current dest_fmt and the + * dest_fmt is a supported one we will align the resolution (see + * libv4lconvert_try_fmt). We do this here too, in case dest_fmt gets + * set without having gone through libv4lconvert_try_fmt, so that a + * try_fmt on the result of a get_fmt always returns the same result. + */ + if (v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) { + dest_fmt->fmt.pix.width &= ~7; + dest_fmt->fmt.pix.height &= ~1; + } + + /* Sigh some drivers (pwc) do not properly reflect what one really gets + after a s_fmt in their try_fmt answer. So update dest format (which we + report as result from s_fmt / g_fmt to the app) with all info from the src + format not changed by conversion */ + dest_fmt->fmt.pix.field = src_fmt->fmt.pix.field; + dest_fmt->fmt.pix.colorspace = src_fmt->fmt.pix.colorspace; + dest_fmt->fmt.pix.xfer_func = src_fmt->fmt.pix.xfer_func; + dest_fmt->fmt.pix.ycbcr_enc = src_fmt->fmt.pix.ycbcr_enc; + dest_fmt->fmt.pix.quantization = src_fmt->fmt.pix.quantization; + /* When we're not converting use bytesperline and imagesize from src_fmt */ + if (v4l2_pix_fmt_compat(src_fmt, dest_fmt)) { + dest_fmt->fmt.pix.bytesperline = src_fmt->fmt.pix.bytesperline; + dest_fmt->fmt.pix.sizeimage = src_fmt->fmt.pix.sizeimage; + } else + v4lconvert_fixup_fmt(dest_fmt); + + devices[index].src_fmt = *src_fmt; + devices[index].dest_fmt = *dest_fmt; + /* round up to full page size */ + devices[index].convert_mmap_frame_size = + (((dest_fmt->fmt.pix.sizeimage + devices[index].page_size - 1) + / devices[index].page_size) * devices[index].page_size); +} + +static int v4l2_s_fmt(int index, struct v4l2_format *dest_fmt) +{ + struct v4l2_format src_fmt; + struct v4l2_pix_format req_pix_fmt; + int result; + + if (v4l2_log_file) { + int pixfmt = dest_fmt->fmt.pix.pixelformat; + + fprintf(v4l2_log_file, "VIDIOC_S_FMT app requesting: %c%c%c%c\n", + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24); + } + + result = v4lconvert_try_format(devices[index].convert, + dest_fmt, &src_fmt); + if (result) { + int saved_err = errno; + V4L2_LOG("S_FMT error trying format: %s\n", strerror(errno)); + errno = saved_err; + return result; + } + + if (src_fmt.fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat && + v4l2_log_file) { + int pixfmt = src_fmt.fmt.pix.pixelformat; + + fprintf(v4l2_log_file, + "VIDIOC_S_FMT converting from: %c%c%c%c\n", + pixfmt & 0xff, (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, pixfmt >> 24); + } + + result = v4l2_check_buffer_change_ok(index); + if (result) + return result; + + req_pix_fmt = src_fmt.fmt.pix; + result = devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, + VIDIOC_S_FMT, &src_fmt); + if (result) { + int saved_err = errno; + V4L2_PERROR("setting pixformat"); + /* Report to the app dest_fmt has not changed */ + *dest_fmt = devices[index].dest_fmt; + errno = saved_err; + return result; + } + + /* See if we've gotten what try_fmt promised us + (this check should never fail) */ + if (src_fmt.fmt.pix.width != req_pix_fmt.width || + src_fmt.fmt.pix.height != req_pix_fmt.height || + src_fmt.fmt.pix.pixelformat != req_pix_fmt.pixelformat) { + V4L2_LOG_ERR("set_fmt gave us a different result then try_fmt!\n"); + /* Not what we expected / wanted, disable conversion */ + *dest_fmt = src_fmt; + } + + v4l2_set_src_and_dest_format(index, &src_fmt, dest_fmt); + + if (devices[index].flags & V4L2_SUPPORTS_TIMEPERFRAME) { + struct v4l2_streamparm parm = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, + VIDIOC_G_PARM, &parm)) + return 0; + v4l2_update_fps(index, &parm); + } + + return 0; +} + +int v4l2_ioctl(int fd, unsigned long int request, ...) +{ + void *arg; + va_list ap; + int result, index, saved_err; + int is_capture_request = 0, stream_needs_locking = 0; + + va_start(ap, request); + arg = va_arg(ap, void *); + va_end(ap); + + index = v4l2_get_index(fd); + if (index == -1) + return SYS_IOCTL(fd, request, arg); + + /* Apparently the kernel and / or glibc ignore the 32 most significant bits + when long = 64 bits, and some applications pass an int holding the req to + ioctl, causing it to get sign extended, depending upon this behavior */ + request = (unsigned int)request; + + if (devices[index].convert == NULL) + goto no_capture_request; + + /* Is this a capture request and do we need to take the stream lock? */ + switch (request) { + case VIDIOC_QUERYCAP: + case VIDIOC_QUERYCTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_CTRL: + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_ENUM_FRAMESIZES: + case VIDIOC_ENUM_FRAMEINTERVALS: + is_capture_request = 1; + break; + case VIDIOC_ENUM_FMT: + if (((struct v4l2_fmtdesc *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) + is_capture_request = 1; + break; + case VIDIOC_TRY_FMT: + if (((struct v4l2_format *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) + is_capture_request = 1; + break; + case VIDIOC_S_FMT: + case VIDIOC_G_FMT: + if (((struct v4l2_format *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; + case VIDIOC_REQBUFS: + if (((struct v4l2_requestbuffers *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + if (((struct v4l2_buffer *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + if (*((enum v4l2_buf_type *)arg) == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + stream_needs_locking = 1; + } + break; + case VIDIOC_S_PARM: + if (((struct v4l2_streamparm *)arg)->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + is_capture_request = 1; + if (devices[index].flags & V4L2_SUPPORTS_TIMEPERFRAME) + stream_needs_locking = 1; + } + break; + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_DV_TIMINGS: + is_capture_request = 1; + stream_needs_locking = 1; + break; + } + + if (!is_capture_request) { +no_capture_request: + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, request, arg); + saved_err = errno; + v4l2_log_ioctl(request, arg, result); + errno = saved_err; + return result; + } + + + if (stream_needs_locking) { + pthread_mutex_lock(&devices[index].stream_lock); + /* If this is the first stream-related ioctl, and we should only allow + libv4lconvert supported destination formats (so that it can do flipping, + processing, etc.) and the current destination format is not supported, + try setting the format to RGB24 (which is a supported dest. format). */ + if (!(devices[index].flags & V4L2_STREAM_TOUCHED) && + v4lconvert_supported_dst_fmt_only(devices[index].convert) && + !v4lconvert_supported_dst_format( + devices[index].dest_fmt.fmt.pix.pixelformat)) { + struct v4l2_format fmt = devices[index].dest_fmt; + + V4L2_LOG("Setting pixelformat to RGB24 (supported_dst_fmt_only)"); + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + v4l2_s_fmt(index, &fmt); + V4L2_LOG("Done setting pixelformat (supported_dst_fmt_only)"); + } + devices[index].flags |= V4L2_STREAM_TOUCHED; + } + + switch (request) { + case VIDIOC_QUERYCTRL: + result = v4lconvert_vidioc_queryctrl(devices[index].convert, arg); + break; + + case VIDIOC_G_CTRL: + result = v4lconvert_vidioc_g_ctrl(devices[index].convert, arg); + break; + + case VIDIOC_S_CTRL: + result = v4lconvert_vidioc_s_ctrl(devices[index].convert, arg); + break; + + case VIDIOC_G_EXT_CTRLS: + result = v4lconvert_vidioc_g_ext_ctrls(devices[index].convert, arg); + break; + + case VIDIOC_TRY_EXT_CTRLS: + result = v4lconvert_vidioc_try_ext_ctrls(devices[index].convert, arg); + break; + + case VIDIOC_S_EXT_CTRLS: + result = v4lconvert_vidioc_s_ext_ctrls(devices[index].convert, arg); + break; + + case VIDIOC_QUERYCAP: { + struct v4l2_capability *cap = arg; + + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_QUERYCAP, cap); + if (result == 0) { + /* We always support read() as we fake it using mmap mode */ + cap->capabilities |= V4L2_CAP_READWRITE; + cap->device_caps |= V4L2_CAP_READWRITE; + } + break; + } + + case VIDIOC_ENUM_FMT: + result = v4lconvert_enum_fmt(devices[index].convert, arg); + break; + + case VIDIOC_ENUM_FRAMESIZES: + result = v4lconvert_enum_framesizes(devices[index].convert, arg); + break; + + case VIDIOC_ENUM_FRAMEINTERVALS: + result = v4lconvert_enum_frameintervals(devices[index].convert, arg); + if (result) + V4L2_LOG("ENUM_FRAMEINTERVALS Error: %s", + v4lconvert_get_error_message(devices[index].convert)); + break; + + case VIDIOC_TRY_FMT: + result = v4lconvert_try_format(devices[index].convert, + arg, NULL); + break; + + case VIDIOC_S_FMT: + result = v4l2_s_fmt(index, arg); + break; + + case VIDIOC_G_FMT: { + struct v4l2_format *fmt = arg; + + *fmt = devices[index].dest_fmt; + result = 0; + break; + } + + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_DV_TIMINGS: { + struct v4l2_format src_fmt = { 0 }; + unsigned int orig_dest_pixelformat = + devices[index].dest_fmt.fmt.pix.pixelformat; + + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, request, arg); + if (result) + break; + + /* These ioctls may have changed the device's fmt */ + src_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_G_FMT, &src_fmt); + if (result) { + V4L2_PERROR("getting pixformat after %s", + v4l2_ioctls[_IOC_NR(request)]); + result = 0; /* The original command did succeed */ + break; + } + + if (v4l2_pix_fmt_compat(&devices[index].src_fmt, &src_fmt)) { + v4l2_set_src_and_dest_format(index, &src_fmt, + &devices[index].dest_fmt); + break; + } + + /* The fmt has been changed, remember the new format ... */ + devices[index].src_fmt = src_fmt; + devices[index].dest_fmt = src_fmt; + v4l2_set_src_and_dest_format(index, &devices[index].src_fmt, + &devices[index].dest_fmt); + /* and try to restore the last set destination pixelformat. */ + src_fmt.fmt.pix.pixelformat = orig_dest_pixelformat; + result = v4l2_s_fmt(index, &src_fmt); + if (result) { + V4L2_LOG_WARN("restoring destination pixelformat after %s failed\n", + v4l2_ioctls[_IOC_NR(request)]); + result = 0; /* The original command did succeed */ + } + + break; + } + + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *req = arg; + + /* IMPROVEME (maybe?) add support for userptr's? */ + if (req->memory != V4L2_MEMORY_MMAP) { + errno = EINVAL; + result = -1; + break; + } + + result = v4l2_check_buffer_change_ok(index); + if (result) + break; + + /* No more buffers then we can manage please */ + if (req->count > V4L2_MAX_NO_FRAMES) + req->count = V4L2_MAX_NO_FRAMES; + + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_REQBUFS, req); + if (result < 0) + break; + result = 0; /* some drivers return the number of buffers on success */ + + devices[index].no_frames = MIN(req->count, V4L2_MAX_NO_FRAMES); + devices[index].flags &= ~V4L2_BUFFERS_REQUESTED_BY_READ; + break; + } + + case VIDIOC_QUERYBUF: { + struct v4l2_buffer *buf = arg; + + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + result = v4l2_deactivate_read_stream(index); + if (result) + break; + } + + /* Do a real query even when converting to let the driver fill in + things like buf->field */ + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_QUERYBUF, buf); + + v4l2_set_conversion_buf_params(index, buf); + break; + } + + case VIDIOC_QBUF: { + struct v4l2_buffer *buf = arg; + + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + result = v4l2_deactivate_read_stream(index); + if (result) + break; + } + + /* With some drivers the buffers must be mapped before queuing */ + if (v4l2_needs_conversion(index)) { + result = v4l2_map_buffers(index); + if (result) + break; + } + + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_QBUF, arg); + + v4l2_set_conversion_buf_params(index, buf); + break; + } + + case VIDIOC_DQBUF: { + struct v4l2_buffer *buf = arg; + + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + result = v4l2_deactivate_read_stream(index); + if (result) + break; + } + + if (!v4l2_needs_conversion(index)) { + pthread_mutex_unlock(&devices[index].stream_lock); + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_DQBUF, buf); + pthread_mutex_lock(&devices[index].stream_lock); + if (result) { + saved_err = errno; + V4L2_PERROR("dequeuing buf"); + errno = saved_err; + } + break; + } + + /* An application can do a DQBUF before mmap-ing in the buffer, + but we need the buffer _now_ to write our converted data + to it! */ + result = v4l2_ensure_convert_mmap_buf(index); + if (result) + break; + + result = v4l2_dequeue_and_convert(index, buf, 0, + devices[index].convert_mmap_frame_size); + if (result >= 0) { + buf->bytesused = result; + result = 0; + } + + v4l2_set_conversion_buf_params(index, buf); + break; + } + + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + if (devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) { + result = v4l2_deactivate_read_stream(index); + if (result) + break; + } + + if (request == VIDIOC_STREAMON) + result = v4l2_streamon(index); + else + result = v4l2_streamoff(index); + break; + + case VIDIOC_S_PARM: { + struct v4l2_streamparm *parm = arg; + + /* See if libv4lconvert wishes to use a different src_fmt + for the new frame rate and set that first */ + if ((devices[index].flags & V4L2_SUPPORTS_TIMEPERFRAME) && + parm->parm.capture.timeperframe.numerator != 0) { + int fps = parm->parm.capture.timeperframe.denominator; + fps += parm->parm.capture.timeperframe.numerator - 1; + fps /= parm->parm.capture.timeperframe.numerator; + v4l2_adjust_src_fmt_to_fps(index, fps); + } + + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, VIDIOC_S_PARM, parm); + if (result) + break; + + v4l2_update_fps(index, parm); + break; + } + + default: + result = devices[index].dev_ops->ioctl( + devices[index].dev_ops_priv, + fd, request, arg); + break; + } + + if (stream_needs_locking) + pthread_mutex_unlock(&devices[index].stream_lock); + + saved_err = errno; + v4l2_log_ioctl(request, arg, result); + errno = saved_err; + + return result; +} + +static void v4l2_adjust_src_fmt_to_fps(int index, int fps) +{ + struct v4l2_pix_format req_pix_fmt; + struct v4l2_format src_fmt; + struct v4l2_format dest_fmt = devices[index].dest_fmt; + struct v4l2_format orig_src_fmt = devices[index].src_fmt; + struct v4l2_format orig_dest_fmt = devices[index].dest_fmt; + int r; + + if (fps == devices[index].fps) + return; + + if (v4l2_check_buffer_change_ok(index)) + return; + + v4lconvert_set_fps(devices[index].convert, fps); + r = v4lconvert_try_format(devices[index].convert, &dest_fmt, &src_fmt); + v4lconvert_set_fps(devices[index].convert, V4L2_DEFAULT_FPS); + if (r) + return; + + if (orig_src_fmt.fmt.pix.pixelformat == src_fmt.fmt.pix.pixelformat || + !v4l2_pix_fmt_compat(&orig_dest_fmt, &dest_fmt)) + return; + + req_pix_fmt = src_fmt.fmt.pix; + if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_S_FMT, &src_fmt)) + return; + + v4l2_set_src_and_dest_format(index, &src_fmt, &dest_fmt); + + /* Check we've gotten what try_fmt promised us and that the + new dest fmt matches the original, if this is true we're done. */ + if (src_fmt.fmt.pix.width == req_pix_fmt.width && + src_fmt.fmt.pix.height == req_pix_fmt.height && + src_fmt.fmt.pix.pixelformat == req_pix_fmt.pixelformat && + v4l2_pix_fmt_identical(&orig_dest_fmt, &dest_fmt)) { + if (v4l2_log_file) { + int pixfmt = src_fmt.fmt.pix.pixelformat; + fprintf(v4l2_log_file, + "new src fmt for fps change: %c%c%c%c\n", + pixfmt & 0xff, (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, pixfmt >> 24); + } + return; + } + + /* Not identical!! */ + V4L2_LOG_WARN("dest fmt changed after adjusting src fmt for fps " + "change, restoring original src fmt"); + src_fmt = orig_src_fmt; + dest_fmt = orig_dest_fmt; + req_pix_fmt = src_fmt.fmt.pix; + if (devices[index].dev_ops->ioctl(devices[index].dev_ops_priv, + devices[index].fd, VIDIOC_S_FMT, &src_fmt)) { + V4L2_PERROR("restoring src fmt"); + return; + } + v4l2_set_src_and_dest_format(index, &src_fmt, &dest_fmt); + if (src_fmt.fmt.pix.width != req_pix_fmt.width || + src_fmt.fmt.pix.height != req_pix_fmt.height || + src_fmt.fmt.pix.pixelformat != req_pix_fmt.pixelformat || + !v4l2_pix_fmt_identical(&orig_dest_fmt, &dest_fmt)) + V4L2_LOG_ERR("dest fmt different after restoring src fmt"); +} + +ssize_t v4l2_read(int fd, void *dest, size_t n) +{ + ssize_t result; + int saved_errno; + int index = v4l2_get_index(fd); + + if (index == -1) + return SYS_READ(fd, dest, n); + + if (!devices[index].dev_ops->read) { + errno = EINVAL; + return -1; + } + + pthread_mutex_lock(&devices[index].stream_lock); + + /* When not converting and the device supports read(), let the kernel handle + it */ + if (devices[index].convert == NULL || + ((devices[index].flags & V4L2_SUPPORTS_READ) && + !v4l2_needs_conversion(index))) { + result = devices[index].dev_ops->read( + devices[index].dev_ops_priv, + fd, dest, n); + goto leave; + } + + /* Since we need to do conversion try to use mmap (streaming) mode under + the hood as that safes a memcpy for each frame read. + + Note sometimes this will fail as some drivers (at least gspca) do not allow + switching from read mode to mmap mode and they assume read() mode if a + select or poll() is done before any buffers are requested. So using mmap + mode under the hood will fail if a select() or poll() is done before the + first emulated read() call. */ + if (!(devices[index].flags & V4L2_STREAM_CONTROLLED_BY_READ) && + !(devices[index].flags & V4L2_USE_READ_FOR_READ)) { + result = v4l2_activate_read_stream(index); + if (result) { + /* Activating mmap mode failed, use read() instead */ + devices[index].flags |= V4L2_USE_READ_FOR_READ; + /* The read call done by v4l2_read_and_convert will start the stream */ + devices[index].first_frame = V4L2_IGNORE_FIRST_FRAME_ERRORS; + } + } + + if (devices[index].flags & V4L2_USE_READ_FOR_READ) { + result = v4l2_read_and_convert(index, dest, n); + } else { + struct v4l2_buffer buf; + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + result = v4l2_dequeue_and_convert(index, &buf, dest, n); + + if (result >= 0) + v4l2_queue_read_buffer(index, buf.index); + } + +leave: + saved_errno = errno; + pthread_mutex_unlock(&devices[index].stream_lock); + errno = saved_errno; + + return result; +} + +ssize_t v4l2_write(int fd, const void *buffer, size_t n) +{ + int index = v4l2_get_index(fd); + + if (index == -1) + return SYS_WRITE(fd, buffer, n); + + if (!devices[index].dev_ops->write) { + errno = EINVAL; + return -1; + } + + return devices[index].dev_ops->write( + devices[index].dev_ops_priv, fd, buffer, n); +} + +void *v4l2_mmap(void *start, size_t length, int prot, int flags, int fd, + int64_t offset) +{ + int index; + unsigned int buffer_index; + void *result; + + index = v4l2_get_index(fd); + if (index == -1 || + /* Check if the mmap data matches our answer to QUERY_BUF. If it doesn't, + let the kernel handle it (to allow for mmap-based non capture use) */ + start || length != devices[index].convert_mmap_frame_size || + ((unsigned int)offset & ~0xFFu) != V4L2_MMAP_OFFSET_MAGIC) { + if (index != -1) + V4L2_LOG("Passing mmap(%p, %d, ..., %x, through to the driver\n", + start, (int)length, (int)offset); + + if (offset & ((1 << MMAP2_PAGE_SHIFT) - 1)) { + errno = EINVAL; + return MAP_FAILED; + } + + return (void *)SYS_MMAP(start, length, prot, flags, fd, offset); + } + + pthread_mutex_lock(&devices[index].stream_lock); + + buffer_index = offset & 0xff; + if (buffer_index >= devices[index].no_frames || + /* Got magic offset and not converting ?? */ + !v4l2_needs_conversion(index)) { + errno = EINVAL; + result = MAP_FAILED; + goto leave; + } + + if (v4l2_ensure_convert_mmap_buf(index)) { + errno = EINVAL; + result = MAP_FAILED; + goto leave; + } + + devices[index].frame_map_count[buffer_index]++; + + result = devices[index].convert_mmap_buf + + buffer_index * devices[index].convert_mmap_frame_size; + + V4L2_LOG("Fake (conversion) mmap buf %u, seen by app at: %p\n", + buffer_index, result); + +leave: + pthread_mutex_unlock(&devices[index].stream_lock); + + return result; +} + +int v4l2_munmap(void *_start, size_t length) +{ + int index; + unsigned int buffer_index; + unsigned char *start = _start; + + /* Is this memory ours? */ + if (start != MAP_FAILED) { + for (index = 0; index < devices_used; index++) + if (devices[index].fd != -1 && + devices[index].convert_mmap_buf != MAP_FAILED && + length == devices[index].convert_mmap_frame_size && + start >= devices[index].convert_mmap_buf && + (start - devices[index].convert_mmap_buf) % length == 0) + break; + + if (index != devices_used) { + int unmapped = 0; + + pthread_mutex_lock(&devices[index].stream_lock); + + buffer_index = (start - devices[index].convert_mmap_buf) / length; + + /* Re-do our checks now that we have the lock, things may have changed */ + if (devices[index].convert_mmap_buf != MAP_FAILED && + length == devices[index].convert_mmap_frame_size && + start >= devices[index].convert_mmap_buf && + (start - devices[index].convert_mmap_buf) % length == 0 && + buffer_index < devices[index].no_frames) { + if (devices[index].frame_map_count[buffer_index] > 0) + devices[index].frame_map_count[buffer_index]--; + unmapped = 1; + } + + pthread_mutex_unlock(&devices[index].stream_lock); + + if (unmapped) { + V4L2_LOG("v4l2 fake buffer munmap %p, %d\n", start, (int)length); + return 0; + } + } + } + + V4L2_LOG("v4l2 unknown munmap %p, %d\n", start, (int)length); + + return SYS_MUNMAP(_start, length); +} + +/* Misc utility functions */ +int v4l2_set_control(int fd, int cid, int value) +{ + struct v4l2_queryctrl qctrl = { .id = cid }; + struct v4l2_control ctrl = { .id = cid }; + int index, result; + + index = v4l2_get_index(fd); + if (index == -1 || devices[index].convert == NULL) { + V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd); + errno = EBADF; + return -1; + } + + result = v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl); + if (result) + return result; + + if (!(qctrl.flags & V4L2_CTRL_FLAG_DISABLED) && + !(qctrl.flags & V4L2_CTRL_FLAG_GRABBED)) { + if (qctrl.type == V4L2_CTRL_TYPE_BOOLEAN) + ctrl.value = value ? 1 : 0; + else + ctrl.value = (value * (qctrl.maximum - qctrl.minimum) + 32767) / 65535 + + qctrl.minimum; + + result = v4lconvert_vidioc_s_ctrl(devices[index].convert, &ctrl); + } + + return result; +} + +int v4l2_get_control(int fd, int cid) +{ + struct v4l2_queryctrl qctrl = { .id = cid }; + struct v4l2_control ctrl = { .id = cid }; + int index = v4l2_get_index(fd); + + if (index == -1 || devices[index].convert == NULL) { + V4L2_LOG_ERR("v4l2_set_control called with invalid fd: %d\n", fd); + errno = EBADF; + return -1; + } + + if (v4lconvert_vidioc_queryctrl(devices[index].convert, &qctrl)) + return -1; + + if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) { + errno = EINVAL; + return -1; + } + + if (v4lconvert_vidioc_g_ctrl(devices[index].convert, &ctrl)) + return -1; + + return ((ctrl.value - qctrl.minimum) * 65535 + + (qctrl.maximum - qctrl.minimum) / 2) / + (qctrl.maximum - qctrl.minimum); +} diff --git a/libv4l2/libv4l2.export b/libv4l2/libv4l2.export new file mode 100644 index 0000000..28f5f3b --- /dev/null +++ b/libv4l2/libv4l2.export @@ -0,0 +1,13 @@ +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +v4l2_close +v4l2_dup +v4l2_fd_open +v4l2_get_control +v4l2_ioctl +v4l2_log_file +v4l2_mmap +v4l2_munmap +v4l2_open +v4l2_read +v4l2_set_control +v4l2_write diff --git a/libv4l2/libv4l2.pc.in b/libv4l2/libv4l2.pc.in new file mode 100644 index 0000000..37ba4fe --- /dev/null +++ b/libv4l2/libv4l2.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +includedir=@includedir@ +libdir=@libdir@ + +Name: libv4l2 +Description: v4l2 device access library +Version: @PACKAGE_VERSION@ +Requires.private: libv4lconvert +Libs: -L${libdir} -lv4l2 +Libs.private: -lpthread +Cflags: -I${includedir} diff --git a/libv4l2/log.c b/libv4l2/log.c new file mode 100644 index 0000000..091b16c --- /dev/null +++ b/libv4l2/log.c @@ -0,0 +1,259 @@ +/* +# (C) 2008 Elmar Kleijn +# (C) 2008 Sjoerd Piepenbrink +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#include +#include "../libv4lconvert/libv4lsyscall-priv.h" +#include +#include "libv4l2.h" +#include "libv4l2-priv.h" + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +FILE *v4l2_log_file = NULL; + +const char *v4l2_ioctls[] = { + /* start v4l2 ioctls */ + [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", + [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", + [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", + [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", + [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", + [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", + [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", + [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", + [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", + [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", + [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", + [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", + [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", + [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", + [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", + [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", + [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", + [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", + [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", + [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", + [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", + [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", + [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", + [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", + [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", + [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", + [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", + [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", + [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", + [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", + [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", + [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", + [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", + [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", + [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", + [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", + [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", + [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", + [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", + [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", + [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", + [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", + [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", + [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", + [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", + [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", + [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", + [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", + [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", + [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", + [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", + [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS", + [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", + [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", + [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", + [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", + [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", + [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX", + [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", + [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", + [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", + [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", + [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", + [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS", + [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS", + [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", + [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", + [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", + [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS", + [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF", + [_IOC_NR(VIDIOC_G_SELECTION)] = "VIDIOC_G_SELECTION", + [_IOC_NR(VIDIOC_S_SELECTION)] = "VIDIOC_S_SELECTION", + [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", + [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", + [_IOC_NR(VIDIOC_ENUM_DV_TIMINGS)] = "VIDIOC_ENUM_DV_TIMINGS", + [_IOC_NR(VIDIOC_QUERY_DV_TIMINGS)] = "VIDIOC_QUERY_DV_TIMINGS", + [_IOC_NR(VIDIOC_DV_TIMINGS_CAP)] = "VIDIOC_DV_TIMINGS_CAP", + [_IOC_NR(VIDIOC_ENUM_FREQ_BANDS)] = "VIDIOC_ENUM_FREQ_BANDS", + [_IOC_NR(VIDIOC_DBG_G_CHIP_INFO)] = "VIDIOC_DBG_G_CHIP_INFO", +}; + +void v4l2_log_ioctl(unsigned long int request, void *arg, int result) +{ + const char *ioctl_str; + char buf[40]; + int saved_errno = errno; + + if (!v4l2_log_file) + return; + + if (_IOC_TYPE(request) == 'V' && _IOC_NR(request) < ARRAY_SIZE(v4l2_ioctls)) + ioctl_str = v4l2_ioctls[_IOC_NR(request)]; + else { + snprintf(buf, sizeof(buf), "unknown request: %c %d", + (int)_IOC_TYPE(request), (int)_IOC_NR(request)); + ioctl_str = buf; + } + + fprintf(v4l2_log_file, "request == %s\n", ioctl_str); + + switch (request) { + case VIDIOC_ENUM_FMT: { + struct v4l2_fmtdesc *fmt = arg; + + fprintf(v4l2_log_file, " index: %u, description: %s\n", + fmt->index, (result < 0) ? "" : (const char *)fmt->description); + break; + } + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: { + struct v4l2_format *fmt = arg; + int pixfmt = fmt->fmt.pix.pixelformat; + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + fprintf(v4l2_log_file, " pixelformat: %c%c%c%c %ux%u\n", + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24, + fmt->fmt.pix.width, + fmt->fmt.pix.height); + fprintf(v4l2_log_file, " field: %d bytesperline: %d imagesize: %d\n", + (int)fmt->fmt.pix.field, (int)fmt->fmt.pix.bytesperline, + (int)fmt->fmt.pix.sizeimage); + fprintf(v4l2_log_file, " colorspace: %d, priv: %x\n", + (int)fmt->fmt.pix.colorspace, (int)fmt->fmt.pix.priv); + } else { + fprintf(v4l2_log_file, " type: %d\n", (int)fmt->type); + } + break; + } + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *req = arg; + + fprintf(v4l2_log_file, " count: %u type: %d memory: %d\n", + req->count, (int)req->type, (int)req->memory); + break; + } + case VIDIOC_DQBUF: { + struct v4l2_buffer *buf = arg; + fprintf(v4l2_log_file, " timestamp %ld.%06ld\n", + (long)buf->timestamp.tv_sec, + (long)buf->timestamp.tv_usec); + break; + } + case VIDIOC_ENUM_FRAMESIZES: { + struct v4l2_frmsizeenum *frmsize = arg; + int pixfmt = frmsize->pixel_format; + + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c\n", + frmsize->index, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24); + switch (frmsize->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + fprintf(v4l2_log_file, " %ux%u\n", frmsize->discrete.width, + frmsize->discrete.height); + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: + fprintf(v4l2_log_file, " %ux%u -> %ux%u\n", + frmsize->stepwise.min_width, frmsize->stepwise.min_height, + frmsize->stepwise.max_width, frmsize->stepwise.max_height); + break; + } + break; + } + case VIDIOC_ENUM_FRAMEINTERVALS: { + struct v4l2_frmivalenum *frmival = arg; + int pixfmt = frmival->pixel_format; + + fprintf(v4l2_log_file, " index: %u pixelformat: %c%c%c%c %ux%u:\n", + frmival->index, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24, + frmival->width, + frmival->height); + switch (frmival->type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + fprintf(v4l2_log_file, " %u/%u\n", frmival->discrete.numerator, + frmival->discrete.denominator); + break; + case V4L2_FRMIVAL_TYPE_CONTINUOUS: + case V4L2_FRMIVAL_TYPE_STEPWISE: + fprintf(v4l2_log_file, " %u/%u -> %u/%u\n", + frmival->stepwise.min.numerator, + frmival->stepwise.min.denominator, + frmival->stepwise.max.numerator, + frmival->stepwise.max.denominator); + break; + } + break; + } + case VIDIOC_G_PARM: + case VIDIOC_S_PARM: { + struct v4l2_streamparm *parm = arg; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + break; + + if (parm->parm.capture.capability & V4L2_CAP_TIMEPERFRAME) + fprintf(v4l2_log_file, "timeperframe: %u/%u\n", + parm->parm.capture.timeperframe.numerator, + parm->parm.capture.timeperframe.denominator); + break; + } + } + + if (result < 0) + fprintf(v4l2_log_file, "result == %d (%s)\n", result, strerror(saved_errno)); + else + fprintf(v4l2_log_file, "result == %d\n", result); + + fflush(v4l2_log_file); +} diff --git a/libv4l2/v4l2-plugin-android.c b/libv4l2/v4l2-plugin-android.c new file mode 100644 index 0000000..22bcf16 --- /dev/null +++ b/libv4l2/v4l2-plugin-android.c @@ -0,0 +1,151 @@ +/* +* Copyright (C) 2010 Nokia Corporation + +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License, version 2.1, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libv4l2.h" +#include "libv4l2-priv.h" +#include "libv4l-plugin.h" + +/* libv4l plugin support: + it is provided by functions v4l2_plugin_[open,close,etc]. + + When open() is called libv4l dlopens files in /usr/lib[64]/libv4l/plugins + 1 at a time and call open callback passing through the applications + parameters unmodified. + + If a plugin is relevant for the specified device node, it can indicate so + by returning a value other then -1 (the actual file descriptor). + As soon as a plugin returns another value then -1 plugin loading stops and + information about it (fd and corresponding library handle) is stored. For + each function v4l2_[ioctl,read,close,etc] is called corresponding + v4l2_plugin_* function which looks if there is loaded plugin for that file + and call it's callbacks. + + v4l2_plugin_* function indicates by it's first argument if plugin was used, + and if it was not then v4l2_* functions proceed with their usual behavior. +*/ + +/* list of plugin search paths */ +static const char *g_plugin_search_paths[] = { + "/system/lib/libv4l/plugins", + "/vendor/lib/libv4l/plugins", + NULL /* list terminator */ +}; + +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret, + const struct libv4l_dev_ops **dev_ops_ret) +{ + char *error; + void *plugin_library = NULL; + const struct libv4l_dev_ops *libv4l2_plugin = NULL; + DIR *plugin_dir = NULL; + struct dirent *entry; + char *suffix = NULL; + int length, i; + char filename[256]; + + /* initialize output params */ + *dev_ops_ret = v4lconvert_get_default_dev_ops(); + *plugin_lib_ret = NULL; + *plugin_priv_ret = NULL; + + /* read the plugin directory for "*.so" files */ + for (i = 0; g_plugin_search_paths[i] != NULL; i++) { + plugin_dir = opendir(g_plugin_search_paths[i]); + if (plugin_dir == NULL) { + V4L2_LOG_ERR("PLUGIN: opening plugin directory (%s) failed\n", + g_plugin_search_paths[i]); + continue; + } + + while ((entry = readdir(plugin_dir))) { + /* get last 3 letter suffix from the filename */ + length = strlen(entry->d_name); + if (length > 3) + suffix = entry->d_name + (length - 3); + + if (!suffix || strcmp(suffix, ".so")) { + suffix = NULL; /* reset for next iteration */ + continue; + } + + /* load library and get desired symbol */ + sprintf(filename, "%s/%s", g_plugin_search_paths[i], entry->d_name); + V4L2_LOG("PLUGIN: dlopen(%s);\n", filename); + plugin_library = dlopen(filename, RTLD_LAZY); + if (!plugin_library) + continue; + + dlerror(); /* Clear any existing error */ + libv4l2_plugin = (struct libv4l_dev_ops *) + dlsym(plugin_library, "libv4l2_plugin"); + error = dlerror(); + if (error != NULL) { + V4L2_LOG_ERR("PLUGIN: dlsym failed: %s\n", error); + dlclose(plugin_library); + continue; + } + + if (!libv4l2_plugin->init || + !libv4l2_plugin->close || + !libv4l2_plugin->ioctl) { + V4L2_LOG("PLUGIN: does not have all mandatory ops\n"); + dlclose(plugin_library); + continue; + } + + *plugin_priv_ret = libv4l2_plugin->init(fd); + if (!*plugin_priv_ret) { + V4L2_LOG("PLUGIN: plugin open() returned NULL\n"); + dlclose(plugin_library); + continue; + } + + /* exit loop when a suitable plugin is found */ + *plugin_lib_ret = plugin_library; + *dev_ops_ret = libv4l2_plugin; + break; + } + closedir(plugin_dir); + + /* exit loop when a suitable plugin is found */ + if (*plugin_lib_ret && *plugin_priv_ret && *dev_ops_ret) + break; + } +} + +void v4l2_plugin_cleanup(void *plugin_lib, void *plugin_priv, + const struct libv4l_dev_ops *dev_ops) +{ + if (plugin_lib) { + dev_ops->close(plugin_priv); + dlclose(plugin_lib); + } +} diff --git a/libv4l2/v4l2-plugin.c b/libv4l2/v4l2-plugin.c new file mode 100644 index 0000000..ed6c897 --- /dev/null +++ b/libv4l2/v4l2-plugin.c @@ -0,0 +1,120 @@ +/* +* Copyright (C) 2010 Nokia Corporation + +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License, version 2.1, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "libv4l2.h" +#include "libv4l2-priv.h" +#include "libv4l-plugin.h" + +/* libv4l plugin support: + it is provided by functions v4l2_plugin_[open,close,etc]. + + When open() is called libv4l dlopens files in /usr/lib[64]/libv4l/plugins + 1 at a time and call open callback passing through the applications + parameters unmodified. + + If a plugin is relevant for the specified device node, it can indicate so + by returning a value other then -1 (the actual file descriptor). + As soon as a plugin returns another value then -1 plugin loading stops and + information about it (fd and corresponding library handle) is stored. For + each function v4l2_[ioctl,read,close,etc] is called corresponding + v4l2_plugin_* function which looks if there is loaded plugin for that file + and call it's callbacks. + + v4l2_plugin_* function indicates by it's first argument if plugin was used, + and if it was not then v4l2_* functions proceed with their usual behavior. +*/ + +#define PLUGINS_PATTERN LIBV4L2_PLUGIN_DIR "/*.so" + +void v4l2_plugin_init(int fd, void **plugin_lib_ret, void **plugin_priv_ret, + const struct libv4l_dev_ops **dev_ops_ret) +{ + char *error; + int glob_ret; + uint32_t i; + void *plugin_library = NULL; + const struct libv4l_dev_ops *libv4l2_plugin = NULL; + glob_t globbuf; + + *dev_ops_ret = v4lconvert_get_default_dev_ops(); + *plugin_lib_ret = NULL; + *plugin_priv_ret = NULL; + + glob_ret = glob(PLUGINS_PATTERN, 0, NULL, &globbuf); + + if (glob_ret == GLOB_NOSPACE) + return; + + if (glob_ret == GLOB_ABORTED || glob_ret == GLOB_NOMATCH) + goto leave; + + for (i = 0; i < globbuf.gl_pathc; i++) { + V4L2_LOG("PLUGIN: dlopen(%s);\n", globbuf.gl_pathv[i]); + + plugin_library = dlopen(globbuf.gl_pathv[i], RTLD_LAZY); + if (!plugin_library) + continue; + + dlerror(); /* Clear any existing error */ + libv4l2_plugin = (struct libv4l_dev_ops *) + dlsym(plugin_library, "libv4l2_plugin"); + error = dlerror(); + if (error != NULL) { + V4L2_LOG_ERR("PLUGIN: dlsym failed: %s\n", error); + dlclose(plugin_library); + continue; + } + + if (!libv4l2_plugin->init || + !libv4l2_plugin->close || + !libv4l2_plugin->ioctl) { + V4L2_LOG("PLUGIN: does not have all mandatory ops\n"); + dlclose(plugin_library); + continue; + } + + *plugin_priv_ret = libv4l2_plugin->init(fd); + if (!*plugin_priv_ret) { + V4L2_LOG("PLUGIN: plugin open() returned NULL\n"); + dlclose(plugin_library); + continue; + } + + *plugin_lib_ret = plugin_library; + *dev_ops_ret = libv4l2_plugin; + break; + } + +leave: + globfree(&globbuf); +} + +void v4l2_plugin_cleanup(void *plugin_lib, void *plugin_priv, + const struct libv4l_dev_ops *dev_ops) +{ + if (plugin_lib) { + dev_ops->close(plugin_priv); + dlclose(plugin_lib); + } +} diff --git a/libv4l2/v4l2convert.c b/libv4l2/v4l2convert.c new file mode 100644 index 0000000..c233ca6 --- /dev/null +++ b/libv4l2/v4l2convert.c @@ -0,0 +1,167 @@ +/* +# open/close/ioctl/mmap/munmap library call wrapper doing format conversion +# for v4l2 applications which want to be able to simply capture bgr24 / yuv420 +# from v4l2 devices with more exotic frame formats. + +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA +*/ + +/* prevent GCC 4.7 inlining error */ +#undef _FORTIFY_SOURCE + +#define _LARGEFILE64_SOURCE 1 + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "../libv4lconvert/libv4lsyscall-priv.h" + +/* Check that open/read/mmap is not a define */ +#if defined open || defined read || defined mmap +#error open/read/mmap is a prepocessor macro !! +#endif + +#if HAVE_VISIBILITY +#define LIBV4L_PUBLIC __attribute__ ((visibility("default"))) +#else +#define LIBV4L_PUBLIC +#endif + +LIBV4L_PUBLIC int open(const char *file, int oflag, ...) +{ + int fd; + int v4l_device = 0; + + /* check if we're opening a video4linux2 device */ + if (!strncmp(file, "/dev/video", 10) || !strncmp(file, "/dev/v4l/", 9)) { + /* Some apps open the device read-only, but we need rw rights as the + buffers *MUST* be mapped rw */ + oflag = (oflag & ~O_ACCMODE) | O_RDWR; + v4l_device = 1; + } + + /* original open code */ + if (oflag & O_CREAT) { + va_list ap; + mode_t mode; + + va_start(ap, oflag); + mode = va_arg(ap, PROMOTED_MODE_T); + + fd = SYS_OPEN(file, oflag, mode); + + va_end(ap); + } else { + fd = SYS_OPEN(file, oflag, 0); + } + /* end of original open code */ + + if (fd == -1 || !v4l_device) + return fd; + + /* Try to Register with libv4l2 (in case of failure pass the fd to the + application as is) */ + v4l2_fd_open(fd, 0); + + return fd; +} + +#if defined(linux) && defined(__GLIBC__) +LIBV4L_PUBLIC int open64(const char *file, int oflag, ...) +{ + int fd; + + /* original open code */ + if (oflag & O_CREAT) { + va_list ap; + mode_t mode; + + va_start(ap, oflag); + mode = va_arg(ap, PROMOTED_MODE_T); + + fd = open(file, oflag | O_LARGEFILE, mode); + + va_end(ap); + } else { + fd = open(file, oflag | O_LARGEFILE); + } + /* end of original open code */ + + return fd; +} +#endif + +#ifndef ANDROID +LIBV4L_PUBLIC int close(int fd) +{ + return v4l2_close(fd); +} + +LIBV4L_PUBLIC int dup(int fd) +{ + return v4l2_dup(fd); +} + +#ifdef HAVE_POSIX_IOCTL +LIBV4L_PUBLIC int ioctl(int fd, int request, ...) +#else +LIBV4L_PUBLIC int ioctl(int fd, unsigned long int request, ...) +#endif +{ + void *arg; + va_list ap; + + va_start(ap, request); + arg = va_arg(ap, void *); + va_end(ap); + + return v4l2_ioctl(fd, request, arg); +} + +LIBV4L_PUBLIC ssize_t read(int fd, void *buffer, size_t n) +{ + return v4l2_read(fd, buffer, n); +} + +LIBV4L_PUBLIC void *mmap(void *start, size_t length, int prot, int flags, int fd, + off_t offset) +{ + return v4l2_mmap(start, length, prot, flags, fd, offset); +} + +#if defined(linux) && defined(__GLIBC__) +LIBV4L_PUBLIC void *mmap64(void *start, size_t length, int prot, int flags, int fd, + off64_t offset) +{ + return v4l2_mmap(start, length, prot, flags, fd, offset); +} +#endif + +LIBV4L_PUBLIC int munmap(void *start, size_t length) +{ + return v4l2_munmap(start, length); +} +#endif diff --git a/libv4lconvert/Makefile b/libv4lconvert/Makefile new file mode 100644 index 0000000..295aa9e --- /dev/null +++ b/libv4lconvert/Makefile @@ -0,0 +1,67 @@ +############################################################################### +# +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA Corporation is strictly prohibited. +# +############################################################################### + +SO_NAME := libnvv4lconvert.so +DEST_DIR ?= /usr/lib/aarch64-linux-gnu/tegra + +SRCS := libv4lconvert.c tinyjpeg.c sn9c10x.c sn9c20x.c pac207.c mr97310a.c \ + flip.c crop.c jidctflt.c spca561-decompress.c \ + rgbyuv.c sn9c2028-decomp.c spca501.c sq905c.c bayer.c hm12.c \ + stv0680.c cpia1.c se401.c jpgl.c jpeg.c jl2005bcd.c helper.c \ + $(wildcard processing/*.c) \ + $(wildcard control/*.c) + +INCLUDES += -I./ -I../include -I./control -I./processing + +OBJS := $(SRCS:.c=.o) + +CFLAGS := -fPIC + +MACHINE = $(shell uname -m) + +ifeq ($(MACHINE),x86_64) + CFLAGS += -DLIBV4L2_PLUGIN_DIR_PATH_X86 + DEST_DIR ?= /opt/nvidia/deepstream/deepstream-4.0/lib + SYM_LINK_DIR := $(DEST_DIR) +else + DEST_DIR ?= /usr/lib/$(MACHINE)-linux-gnu/tegra + SYM_LINK_DIR := $(shell realpath $(DEST_DIR)/..) +endif + +LIBS = -lrt + +LDFLAGS := -Wl,-soname,libv4lconvert.so.0 + +all: $(SO_NAME) + +%.o: %.c + $(CC) -c $< $(CFLAGS) $(INCLUDES) -o $@ + +$(SO_NAME): $(OBJS) + $(CC) -shared -o $(SO_NAME) $(OBJS) $(LIBS) $(LDFLAGS) + +.PHONY: install +install: $(SO_NAME) + cp -vp $(SO_NAME) $(DEST_DIR) + if [ "$(MACHINE)" = "aarch64" ]; then \ + ln -sf tegra/$(SO_NAME) $(SYM_LINK_DIR)/libv4lconvert.so.0.0.999999 ; \ + else \ + ln -sf $(SO_NAME) $(SYM_LINK_DIR)/libv4lconvert.so.0.0.999999 ; \ + fi + ln -sf libv4lconvert.so.0.0.999999 \ + $(SYM_LINK_DIR)/libv4lconvert.so + ln -sf libv4lconvert.so.0.0.999999 \ + ${SYM_LINK_DIR}/libv4lconvert.so.0 + +.PHONY: clean +clean: + rm -rf $(OBJS) $(SO_NAME) diff --git a/libv4lconvert/Makefile.am b/libv4lconvert/Makefile.am new file mode 100644 index 0000000..5c8a1cf --- /dev/null +++ b/libv4lconvert/Makefile.am @@ -0,0 +1,31 @@ +if WITH_LIBV4L +lib_LTLIBRARIES = libv4lconvert.la +libv4lconvertpriv_PROGRAMS = ov511-decomp ov518-decomp +include_HEADERS = ../include/libv4lconvert.h +pkgconfig_DATA = libv4lconvert.pc +LIBV4LCONVERT_VERSION = -version-info 0 +else +noinst_LTLIBRARIES = libv4lconvert.la +endif + +libv4lconvert_la_SOURCES = \ + libv4lconvert.c tinyjpeg.c sn9c10x.c sn9c20x.c pac207.c mr97310a.c \ + flip.c crop.c jidctflt.c spca561-decompress.c \ + rgbyuv.c sn9c2028-decomp.c spca501.c sq905c.c bayer.c hm12.c \ + stv0680.c cpia1.c se401.c jpgl.c jpeg.c jl2005bcd.c \ + control/libv4lcontrol.c control/libv4lcontrol.h control/libv4lcontrol-priv.h \ + processing/libv4lprocessing.c processing/whitebalance.c processing/autogain.c \ + processing/gamma.c processing/libv4lprocessing.h processing/libv4lprocessing-priv.h \ + helper.c helper-funcs.h libv4lconvert-priv.h libv4lsyscall-priv.h \ + tinyjpeg.h tinyjpeg-internal.h +if HAVE_JPEG +libv4lconvert_la_SOURCES += jpeg_memsrcdest.c jpeg_memsrcdest.h +endif +libv4lconvert_la_CPPFLAGS = $(CFLAG_VISIBILITY) $(ENFORCE_LIBV4L_STATIC) +libv4lconvert_la_LDFLAGS = $(LIBV4LCONVERT_VERSION) -lrt -lm $(JPEG_LIBS) $(ENFORCE_LIBV4L_STATIC) + +ov511_decomp_SOURCES = ov511-decomp.c + +ov518_decomp_SOURCES = ov518-decomp.c + +EXTRA_DIST = Android.mk diff --git a/libv4lconvert/Makefile.dGPU b/libv4lconvert/Makefile.dGPU new file mode 100644 index 0000000..d12c55f --- /dev/null +++ b/libv4lconvert/Makefile.dGPU @@ -0,0 +1,41 @@ +############################################################################### +# +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA Corporation and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA Corporation is strictly prohibited. +# +############################################################################### +CC:=gcc +TARGET_NAME:= libnvv4lconvert.so + +SRCS := libv4lconvert.c tinyjpeg.c sn9c10x.c sn9c20x.c pac207.c mr97310a.c \ + flip.c crop.c jidctflt.c spca561-decompress.c \ + rgbyuv.c sn9c2028-decomp.c spca501.c sq905c.c bayer.c hm12.c \ + stv0680.c cpia1.c se401.c jpgl.c jpeg.c jl2005bcd.c helper.c \ + $(wildcard processing/*.c) \ + $(wildcard control/*.c) + +INC_PATHS:= ../include ./control ./processing + +CFLAGS:= -fPIC +CFLAGS += -DLIBV4L2_PLUGIN_DIR_PATH_X86 + +IGNORE_DS_PACKAGE_NAMING:=1 + +LDFLAGS:= -shared -Wl,-soname,libv4lconvert.so.0 +LIBS:= -lrt -lm + +#IS_V4L2_LIB:=1 +PACKAGE_BINARY_IN_DS:=1 + +BUILD_DIR:=../../../../deepstream/sdk/build/libs/libv4l/ + +include ../../../../deepstream/sdk/Rules.mk + +install:: + ln -sf $(INSTALL_DIR)/$(TARGET_NAME) /usr/lib/x86_64-linux-gnu/libv4lconvert.so.0.0.99999 + ldconfig diff --git a/libv4lconvert/bayer.c b/libv4lconvert/bayer.c new file mode 100644 index 0000000..4cbf397 --- /dev/null +++ b/libv4lconvert/bayer.c @@ -0,0 +1,632 @@ +/* + * lib4lconvert, video4linux2 format conversion lib + * (C) 2008 Hans de Goede + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + * + * Note: original bayer_to_bgr24 code from : + * 1394-Based Digital Camera Control Library + * + * Bayer pattern decoding functions + * + * Written by Damien Douxchamps and Frederic Devernay + * + * Note that the original bayer.c in libdc1394 supports many different + * bayer decode algorithms, for lib4lconvert the one in this file has been + * chosen (and optimized a bit) and the other algorithms have been removed, + * see bayer.c from libdc1394 for all supported algorithms + */ + +#include +#include "libv4lconvert-priv.h" + +/************************************************************** + * Color conversion functions for cameras that can * + * output raw-Bayer pattern images, such as some Basler and * + * Point Grey camera. Most of the algos presented here come * + * from http://www-ise.stanford.edu/~tingchen/ and have been * + * converted from Matlab to C and extended to all elementary * + * patterns. * + **************************************************************/ + +/* inspired by OpenCV's Bayer decoding */ +static void v4lconvert_border_bayer_line_to_bgr24( + const unsigned char *bayer, const unsigned char *adjacent_bayer, + unsigned char *bgr, int width, const int start_with_green, const int blue_line) +{ + int t0, t1; + + if (start_with_green) { + /* First pixel */ + if (blue_line) { + *bgr++ = bayer[1]; + *bgr++ = bayer[0]; + *bgr++ = adjacent_bayer[0]; + } else { + *bgr++ = adjacent_bayer[0]; + *bgr++ = bayer[0]; + *bgr++ = bayer[1]; + } + /* Second pixel */ + t0 = (bayer[0] + bayer[2] + adjacent_bayer[1] + 1) / 3; + t1 = (adjacent_bayer[0] + adjacent_bayer[2] + 1) >> 1; + if (blue_line) { + *bgr++ = bayer[1]; + *bgr++ = t0; + *bgr++ = t1; + } else { + *bgr++ = t1; + *bgr++ = t0; + *bgr++ = bayer[1]; + } + bayer++; + adjacent_bayer++; + width -= 2; + } else { + /* First pixel */ + t0 = (bayer[1] + adjacent_bayer[0] + 1) >> 1; + if (blue_line) { + *bgr++ = bayer[0]; + *bgr++ = t0; + *bgr++ = adjacent_bayer[1]; + } else { + *bgr++ = adjacent_bayer[1]; + *bgr++ = t0; + *bgr++ = bayer[0]; + } + width--; + } + + if (blue_line) { + for ( ; width > 2; width -= 2) { + t0 = (bayer[0] + bayer[2] + 1) >> 1; + *bgr++ = t0; + *bgr++ = bayer[1]; + *bgr++ = adjacent_bayer[1]; + bayer++; + adjacent_bayer++; + + t0 = (bayer[0] + bayer[2] + adjacent_bayer[1] + 1) / 3; + t1 = (adjacent_bayer[0] + adjacent_bayer[2] + 1) >> 1; + *bgr++ = bayer[1]; + *bgr++ = t0; + *bgr++ = t1; + bayer++; + adjacent_bayer++; + } + } else { + for ( ; width > 2; width -= 2) { + t0 = (bayer[0] + bayer[2] + 1) >> 1; + *bgr++ = adjacent_bayer[1]; + *bgr++ = bayer[1]; + *bgr++ = t0; + bayer++; + adjacent_bayer++; + + t0 = (bayer[0] + bayer[2] + adjacent_bayer[1] + 1) / 3; + t1 = (adjacent_bayer[0] + adjacent_bayer[2] + 1) >> 1; + *bgr++ = t1; + *bgr++ = t0; + *bgr++ = bayer[1]; + bayer++; + adjacent_bayer++; + } + } + + if (width == 2) { + /* Second to last pixel */ + t0 = (bayer[0] + bayer[2] + 1) >> 1; + if (blue_line) { + *bgr++ = t0; + *bgr++ = bayer[1]; + *bgr++ = adjacent_bayer[1]; + } else { + *bgr++ = adjacent_bayer[1]; + *bgr++ = bayer[1]; + *bgr++ = t0; + } + /* Last pixel */ + t0 = (bayer[1] + adjacent_bayer[2] + 1) >> 1; + if (blue_line) { + *bgr++ = bayer[2]; + *bgr++ = t0; + *bgr++ = adjacent_bayer[1]; + } else { + *bgr++ = adjacent_bayer[1]; + *bgr++ = t0; + *bgr++ = bayer[2]; + } + } else { + /* Last pixel */ + if (blue_line) { + *bgr++ = bayer[0]; + *bgr++ = bayer[1]; + *bgr++ = adjacent_bayer[1]; + } else { + *bgr++ = adjacent_bayer[1]; + *bgr++ = bayer[1]; + *bgr++ = bayer[0]; + } + } +} + +/* From libdc1394, which on turn was based on OpenCV's Bayer decoding */ +static void bayer_to_rgbbgr24(const unsigned char *bayer, + unsigned char *bgr, int width, int height, const unsigned int stride, unsigned int pixfmt, + int start_with_green, int blue_line) +{ + /* render the first line */ + v4lconvert_border_bayer_line_to_bgr24(bayer, bayer + stride, bgr, width, + start_with_green, blue_line); + bgr += width * 3; + + /* reduce height by 2 because of the special case top/bottom line */ + for (height -= 2; height; height--) { + int t0, t1; + /* (width - 2) because of the border */ + const unsigned char *bayer_end = bayer + (width - 2); + + if (start_with_green) { + + t0 = (bayer[1] + bayer[stride * 2 + 1] + 1) >> 1; + /* Write first pixel */ + t1 = (bayer[0] + bayer[stride * 2] + bayer[stride + 1] + 1) / 3; + if (blue_line) { + *bgr++ = t0; + *bgr++ = t1; + *bgr++ = bayer[stride]; + } else { + *bgr++ = bayer[stride]; + *bgr++ = t1; + *bgr++ = t0; + } + + /* Write second pixel */ + t1 = (bayer[stride] + bayer[stride + 2] + 1) >> 1; + if (blue_line) { + *bgr++ = t0; + *bgr++ = bayer[stride + 1]; + *bgr++ = t1; + } else { + *bgr++ = t1; + *bgr++ = bayer[stride + 1]; + *bgr++ = t0; + } + bayer++; + } else { + /* Write first pixel */ + t0 = (bayer[0] + bayer[stride * 2] + 1) >> 1; + if (blue_line) { + *bgr++ = t0; + *bgr++ = bayer[stride]; + *bgr++ = bayer[stride + 1]; + } else { + *bgr++ = bayer[stride + 1]; + *bgr++ = bayer[stride]; + *bgr++ = t0; + } + } + + if (blue_line) { + for (; bayer <= bayer_end - 2; bayer += 2) { + t0 = (bayer[0] + bayer[2] + bayer[stride * 2] + + bayer[stride * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[stride] + bayer[stride + 2] + + bayer[stride * 2 + 1] + 2) >> 2; + *bgr++ = t0; + *bgr++ = t1; + *bgr++ = bayer[stride + 1]; + + t0 = (bayer[2] + bayer[stride * 2 + 2] + 1) >> 1; + t1 = (bayer[stride + 1] + bayer[stride + 3] + 1) >> 1; + *bgr++ = t0; + *bgr++ = bayer[stride + 2]; + *bgr++ = t1; + } + } else { + for (; bayer <= bayer_end - 2; bayer += 2) { + t0 = (bayer[0] + bayer[2] + bayer[stride * 2] + + bayer[stride * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[stride] + bayer[stride + 2] + + bayer[stride * 2 + 1] + 2) >> 2; + *bgr++ = bayer[stride + 1]; + *bgr++ = t1; + *bgr++ = t0; + + t0 = (bayer[2] + bayer[stride * 2 + 2] + 1) >> 1; + t1 = (bayer[stride + 1] + bayer[stride + 3] + 1) >> 1; + *bgr++ = t1; + *bgr++ = bayer[stride + 2]; + *bgr++ = t0; + } + } + + if (bayer < bayer_end) { + /* write second to last pixel */ + t0 = (bayer[0] + bayer[2] + bayer[stride * 2] + + bayer[stride * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[stride] + bayer[stride + 2] + + bayer[stride * 2 + 1] + 2) >> 2; + if (blue_line) { + *bgr++ = t0; + *bgr++ = t1; + *bgr++ = bayer[stride + 1]; + } else { + *bgr++ = bayer[stride + 1]; + *bgr++ = t1; + *bgr++ = t0; + } + /* write last pixel */ + t0 = (bayer[2] + bayer[stride * 2 + 2] + 1) >> 1; + if (blue_line) { + *bgr++ = t0; + *bgr++ = bayer[stride + 2]; + *bgr++ = bayer[stride + 1]; + } else { + *bgr++ = bayer[stride + 1]; + *bgr++ = bayer[stride + 2]; + *bgr++ = t0; + } + + bayer++; + + } else { + /* write last pixel */ + t0 = (bayer[0] + bayer[stride * 2] + 1) >> 1; + t1 = (bayer[1] + bayer[stride * 2 + 1] + bayer[stride] + 1) / 3; + if (blue_line) { + *bgr++ = t0; + *bgr++ = t1; + *bgr++ = bayer[stride + 1]; + } else { + *bgr++ = bayer[stride + 1]; + *bgr++ = t1; + *bgr++ = t0; + } + + } + + /* skip 2 border pixels and padding */ + bayer += (stride - width) + 2; + + blue_line = !blue_line; + start_with_green = !start_with_green; + } + + /* render the last line */ + v4lconvert_border_bayer_line_to_bgr24(bayer + stride, bayer, bgr, width, + !start_with_green, !blue_line); +} + +void v4lconvert_bayer_to_rgb24(const unsigned char *bayer, + unsigned char *bgr, int width, int height, const unsigned int stride, unsigned int pixfmt) +{ + bayer_to_rgbbgr24(bayer, bgr, width, height, stride, pixfmt, + pixfmt == V4L2_PIX_FMT_SGBRG8 /* start with green */ + || pixfmt == V4L2_PIX_FMT_SGRBG8, + pixfmt != V4L2_PIX_FMT_SBGGR8 /* blue line */ + && pixfmt != V4L2_PIX_FMT_SGBRG8); +} + +void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, + unsigned char *bgr, int width, int height, const unsigned int stride, unsigned int pixfmt) +{ + bayer_to_rgbbgr24(bayer, bgr, width, height, stride, pixfmt, + pixfmt == V4L2_PIX_FMT_SGBRG8 /* start with green */ + || pixfmt == V4L2_PIX_FMT_SGRBG8, + pixfmt == V4L2_PIX_FMT_SBGGR8 /* blue line */ + || pixfmt == V4L2_PIX_FMT_SGBRG8); +} + +static void v4lconvert_border_bayer_line_to_y( + const unsigned char *bayer, const unsigned char *adjacent_bayer, + unsigned char *y, int width, int start_with_green, int blue_line) +{ + int t0, t1; + + if (start_with_green) { + /* First pixel */ + if (blue_line) { + *y++ = (8453 * adjacent_bayer[0] + 16594 * bayer[0] + + 3223 * bayer[1] + 524288) >> 15; + } else { + *y++ = (8453 * bayer[1] + 16594 * bayer[0] + + 3223 * adjacent_bayer[0] + 524288) >> 15; + } + /* Second pixel */ + t0 = bayer[0] + bayer[2] + adjacent_bayer[1]; + t1 = adjacent_bayer[0] + adjacent_bayer[2]; + if (blue_line) + *y++ = (4226 * t1 + 5531 * t0 + 3223 * bayer[1] + 524288) >> 15; + else + *y++ = (8453 * bayer[1] + 5531 * t0 + 1611 * t1 + 524288) >> 15; + bayer++; + adjacent_bayer++; + width -= 2; + } else { + /* First pixel */ + t0 = bayer[1] + adjacent_bayer[0]; + if (blue_line) { + *y++ = (8453 * adjacent_bayer[1] + 8297 * t0 + + 3223 * bayer[0] + 524288) >> 15; + } else { + *y++ = (8453 * bayer[0] + 8297 * t0 + + 3223 * adjacent_bayer[1] + 524288) >> 15; + } + width--; + } + + if (blue_line) { + for ( ; width > 2; width -= 2) { + t0 = bayer[0] + bayer[2]; + *y++ = (8453 * adjacent_bayer[1] + 16594 * bayer[1] + + 1611 * t0 + 524288) >> 15; + bayer++; + adjacent_bayer++; + + t0 = bayer[0] + bayer[2] + adjacent_bayer[1]; + t1 = adjacent_bayer[0] + adjacent_bayer[2]; + *y++ = (4226 * t1 + 5531 * t0 + 3223 * bayer[1] + 524288) >> 15; + bayer++; + adjacent_bayer++; + } + } else { + for ( ; width > 2; width -= 2) { + t0 = bayer[0] + bayer[2]; + *y++ = (4226 * t0 + 16594 * bayer[1] + + 3223 * adjacent_bayer[1] + 524288) >> 15; + bayer++; + adjacent_bayer++; + + t0 = bayer[0] + bayer[2] + adjacent_bayer[1]; + t1 = adjacent_bayer[0] + adjacent_bayer[2]; + *y++ = (8453 * bayer[1] + 5531 * t0 + 1611 * t1 + 524288) >> 15; + bayer++; + adjacent_bayer++; + } + } + + if (width == 2) { + /* Second to last pixel */ + t0 = bayer[0] + bayer[2]; + if (blue_line) { + *y++ = (8453 * adjacent_bayer[1] + 16594 * bayer[1] + + 1611 * t0 + 524288) >> 15; + } else { + *y++ = (4226 * t0 + 16594 * bayer[1] + + 3223 * adjacent_bayer[1] + 524288) >> 15; + } + /* Last pixel */ + t0 = bayer[1] + adjacent_bayer[2]; + if (blue_line) { + *y++ = (8453 * adjacent_bayer[1] + 8297 * t0 + + 3223 * bayer[2] + 524288) >> 15; + } else { + *y++ = (8453 * bayer[2] + 8297 * t0 + + 3223 * adjacent_bayer[1] + 524288) >> 15; + } + } else { + /* Last pixel */ + if (blue_line) { + *y++ = (8453 * adjacent_bayer[1] + 16594 * bayer[1] + + 3223 * bayer[0] + 524288) >> 15; + } else { + *y++ = (8453 * bayer[0] + 16594 * bayer[1] + + 3223 * adjacent_bayer[1] + 524288) >> 15; + } + } +} + +void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, + int width, int height, const unsigned int stride, unsigned int src_pixfmt, int yvu) +{ + int blue_line = 0, start_with_green = 0, x, y; + unsigned char *ydst = yuv; + unsigned char *udst, *vdst; + + if (yvu) { + vdst = yuv + width * height; + udst = vdst + width * height / 4; + } else { + udst = yuv + width * height; + vdst = udst + width * height / 4; + } + + /* First calculate the u and v planes 2x2 pixels at a time */ + switch (src_pixfmt) { + case V4L2_PIX_FMT_SBGGR8: + for (y = 0; y < height; y += 2) { + for (x = 0; x < width; x += 2) { + int b, g, r; + + b = bayer[x]; + g = bayer[x + 1]; + g += bayer[x + stride]; + r = bayer[x + stride + 1]; + *udst++ = (-4878 * r - 4789 * g + 14456 * b + 4210688) >> 15; + *vdst++ = (14456 * r - 6052 * g - 2351 * b + 4210688) >> 15; + } + bayer += 2 * stride; + } + blue_line = 1; + break; + + case V4L2_PIX_FMT_SRGGB8: + for (y = 0; y < height; y += 2) { + for (x = 0; x < width; x += 2) { + int b, g, r; + + r = bayer[x]; + g = bayer[x + 1]; + g += bayer[x + stride]; + b = bayer[x + stride + 1]; + *udst++ = (-4878 * r - 4789 * g + 14456 * b + 4210688) >> 15; + *vdst++ = (14456 * r - 6052 * g - 2351 * b + 4210688) >> 15; + } + bayer += 2 * stride; + } + break; + + case V4L2_PIX_FMT_SGBRG8: + for (y = 0; y < height; y += 2) { + for (x = 0; x < width; x += 2) { + int b, g, r; + + g = bayer[x]; + b = bayer[x + 1]; + r = bayer[x + stride]; + g += bayer[x + stride + 1]; + *udst++ = (-4878 * r - 4789 * g + 14456 * b + 4210688) >> 15; + *vdst++ = (14456 * r - 6052 * g - 2351 * b + 4210688) >> 15; + } + bayer += 2 * stride; + } + blue_line = 1; + start_with_green = 1; + break; + + case V4L2_PIX_FMT_SGRBG8: + for (y = 0; y < height; y += 2) { + for (x = 0; x < width; x += 2) { + int b, g, r; + + g = bayer[x]; + r = bayer[x + 1]; + b = bayer[x + stride]; + g += bayer[x + stride + 1]; + *udst++ = (-4878 * r - 4789 * g + 14456 * b + 4210688) >> 15; + *vdst++ = (14456 * r - 6052 * g - 2351 * b + 4210688) >> 15; + } + bayer += 2 * stride; + } + start_with_green = 1; + break; + } + + /* Point bayer back to start of frame */ + bayer -= stride * height; + + /* render the first line */ + v4lconvert_border_bayer_line_to_y(bayer, bayer + stride, ydst, width, + start_with_green, blue_line); + ydst += width; + + /* reduce height by 2 because of the border */ + for (height -= 2; height; height--) { + int t0, t1; + /* (width - 2) because of the border */ + const unsigned char *bayer_end = bayer + (width - 2); + + if (start_with_green) { + t0 = bayer[1] + bayer[stride * 2 + 1]; + /* Write first pixel */ + t1 = bayer[0] + bayer[stride * 2] + bayer[stride + 1]; + if (blue_line) + *ydst++ = (8453 * bayer[stride] + 5516 * t1 + + 1661 * t0 + 524288) >> 15; + else + *ydst++ = (4226 * t0 + 5516 * t1 + + 3223 * bayer[stride] + 524288) >> 15; + + /* Write second pixel */ + t1 = bayer[stride] + bayer[stride + 2]; + if (blue_line) + *ydst++ = (4226 * t1 + 16594 * bayer[stride + 1] + + 1611 * t0 + 524288) >> 15; + else + *ydst++ = (4226 * t0 + 16594 * bayer[stride + 1] + + 1611 * t1 + 524288) >> 15; + bayer++; + } else { + /* Write first pixel */ + t0 = bayer[0] + bayer[stride * 2]; + if (blue_line) { + *ydst++ = (8453 * bayer[stride + 1] + 16594 * bayer[stride] + + 1661 * t0 + 524288) >> 15; + } else { + *ydst++ = (4226 * t0 + 16594 * bayer[stride] + + 3223 * bayer[stride + 1] + 524288) >> 15; + } + } + + if (blue_line) { + for (; bayer <= bayer_end - 2; bayer += 2) { + t0 = bayer[0] + bayer[2] + bayer[stride * 2] + bayer[stride * 2 + 2]; + t1 = bayer[1] + bayer[stride] + bayer[stride + 2] + bayer[stride * 2 + 1]; + *ydst++ = (8453 * bayer[stride + 1] + 4148 * t1 + + 806 * t0 + 524288) >> 15; + + t0 = bayer[2] + bayer[stride * 2 + 2]; + t1 = bayer[stride + 1] + bayer[stride + 3]; + *ydst++ = (4226 * t1 + 16594 * bayer[stride + 2] + + 1611 * t0 + 524288) >> 15; + } + } else { + for (; bayer <= bayer_end - 2; bayer += 2) { + t0 = bayer[0] + bayer[2] + bayer[stride * 2] + bayer[stride * 2 + 2]; + t1 = bayer[1] + bayer[stride] + bayer[stride + 2] + bayer[stride * 2 + 1]; + *ydst++ = (2113 * t0 + 4148 * t1 + + 3223 * bayer[stride + 1] + 524288) >> 15; + + t0 = bayer[2] + bayer[stride * 2 + 2]; + t1 = bayer[stride + 1] + bayer[stride + 3]; + *ydst++ = (4226 * t0 + 16594 * bayer[stride + 2] + + 1611 * t1 + 524288) >> 15; + } + } + + if (bayer < bayer_end) { + /* Write second to last pixel */ + t0 = bayer[0] + bayer[2] + bayer[stride * 2] + bayer[stride * 2 + 2]; + t1 = bayer[1] + bayer[stride] + bayer[stride + 2] + bayer[stride * 2 + 1]; + if (blue_line) + *ydst++ = (8453 * bayer[stride + 1] + 4148 * t1 + + 806 * t0 + 524288) >> 15; + else + *ydst++ = (2113 * t0 + 4148 * t1 + + 3223 * bayer[stride + 1] + 524288) >> 15; + + /* write last pixel */ + t0 = bayer[2] + bayer[stride * 2 + 2]; + if (blue_line) { + *ydst++ = (8453 * bayer[stride + 1] + 16594 * bayer[stride + 2] + + 1661 * t0 + 524288) >> 15; + } else { + *ydst++ = (4226 * t0 + 16594 * bayer[stride + 2] + + 3223 * bayer[stride + 1] + 524288) >> 15; + } + bayer++; + } else { + /* write last pixel */ + t0 = bayer[0] + bayer[stride * 2]; + t1 = bayer[1] + bayer[stride * 2 + 1] + bayer[stride]; + if (blue_line) + *ydst++ = (8453 * bayer[stride + 1] + 5516 * t1 + + 1661 * t0 + 524288) >> 15; + else + *ydst++ = (4226 * t0 + 5516 * t1 + + 3223 * bayer[stride + 1] + 524288) >> 15; + } + + /* skip 2 border pixels and padding */ + bayer += (stride - width) + 2; + + blue_line = !blue_line; + start_with_green = !start_with_green; + } + + /* render the last line */ + v4lconvert_border_bayer_line_to_y(bayer + stride, bayer, ydst, width, + !start_with_green, !blue_line); +} diff --git a/libv4lconvert/control/libv4lcontrol-priv.h b/libv4lconvert/control/libv4lcontrol-priv.h new file mode 100644 index 0000000..899f763 --- /dev/null +++ b/libv4lconvert/control/libv4lcontrol-priv.h @@ -0,0 +1,79 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Radjnies Bhansingh +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifndef __LIBV4LCONTROL_PRIV_H +#define __LIBV4LCONTROL_PRIV_H + +#include "libv4l-plugin.h" + +#define V4LCONTROL_SHM_SIZE 4096 + +#define V4LCONTROL_SUPPORTS_NEXT_CTRL 0x01 +#define V4LCONTROL_MEMORY_IS_MALLOCED 0x02 + +struct v4lcontrol_flags_info; + +struct v4lcontrol_data { + int fd; /* Device fd */ + int bandwidth; /* Connection bandwidth (0 = unknown) */ + int flags; /* Flags for this device */ + int priv_flags; /* Internal use only flags */ + int controls; /* Which controls to use for this device */ + unsigned int *shm_values; /* shared memory control value store */ + unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */ + const struct v4lcontrol_flags_info *flags_info; + void *dev_ops_priv; + const struct libv4l_dev_ops *dev_ops; +}; + +struct v4lcontrol_flags_info { + unsigned short vendor_id; + unsigned short product_id; + unsigned short product_mask; + const char *dmi_board_vendor; + const char *dmi_board_name; + /* We could also use the USB manufacturer and product strings some devices have + const char *manufacturer; + const char *product; */ + int flags; + int default_gamma; + /* Some seldom used dmi strings (for notebooks with bogus info in the board + entries, but usefull info elsewhere). We keep this at the end as to not + polute the initalizers for the normal case. */ + /* System (product) vendor / name */ + const char *dmi_system_vendor; + const char *dmi_system_name; + /* Board and System versions */ + const char *dmi_board_version; + const char *dmi_system_version; +}; + +struct v4lcontrol_usb_id { + unsigned short vendor_id; + unsigned short product_id; +}; + +struct v4lcontrol_upside_down_table { + const char **board_vendor; + const char **board_name; + const struct v4lcontrol_usb_id *camera_id; +}; + +#endif diff --git a/libv4lconvert/control/libv4lcontrol.c b/libv4lconvert/control/libv4lcontrol.c new file mode 100644 index 0000000..6666238 --- /dev/null +++ b/libv4lconvert/control/libv4lcontrol.c @@ -0,0 +1,1120 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Radjnies Bhansingh +# (C) 2008-2010 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libv4lcontrol.h" +#include "libv4lcontrol-priv.h" +#include "../libv4lsyscall-priv.h" +#include +#include + +#define ARRAY_SIZE(x) ((int)sizeof(x) / (int)sizeof((x)[0])) + +/* List of cams which need special flags */ +static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { + /* First: Upside down devices */ + /* Philips SPC200NC */ + { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + /* Philips SPC300NC */ + { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + /* Philips SPC210NC */ + { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + /* Philips SPC315NC */ + { 0x0471, 0x032e, 0, NULL, NULL, V4LCONTROL_VFLIPPED }, + /* Genius E-M 112 (also want whitebalance by default) */ + { 0x093a, 0x2476, 0, NULL, NULL, + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED | V4LCONTROL_WANTS_WB, 1500 }, + + /* Laptops (and all in one PC's) */ + /* 0x0402, 0x5602 - add quirk to driver/media/video/gspca/m5602/m5602_s5k4aa.c */ + { 0x0402, 0x5606, 0, + "CLEVO CO. ", + "M570TU ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x0402, 0x5606, 0, "AOpen", "i45GMt-HR R1.02 Dec.21.2009", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "RM plc", "RM ONE ECOQUIET 300" }, + { 0x0402, 0x5606, 0, "Intel Corporation", "DQ45CB", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "RM plc", "RM EXPERT 3040" }, + { 0x046d, 0x09b2, 0, "FUJITSU", "FJNB1C9", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU SIEMENS", "LIFEBOOK P7230" }, + { 0x046d, 0x09b2, 0, "FUJITSU", "FJNB1C9", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LifeBook P7230" }, + /* A re-branded ASUS notebook */ + { 0x04f2, 0xb012, 0, "Founder PC", "T14MF", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + /* These 3 PACKARD BELL's seem to be Asus notebook in disguise */ + { 0x04f2, 0xb012, 0, "Packard Bell BV", "T32A ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG45", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG46", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BS45 ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_F0945", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + /* This Asus has its camera the right way up, so we've an entry here + to override the wildcard match from the upside_down table. */ + { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "F3Sc ", + 0 }, + { 0x04f2, 0xb071, 0, "AXIOO", "PICO DJH Model", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "PEGATRON CORPORATION", "H54", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb071, 0, "PEGATRON CORP.", NULL, + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "Philco", "S\202rie PHN10050" }, + { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB206", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LifeBook T4410" }, + { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB219", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK T730" }, + { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB232", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK T731" }, + { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB21A", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK TH700" }, + { 0x04f2, 0xb16b, 0, "ASUSTeK Computer Inc. ", "U20A ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + /* 1 report: + Unknown laptop model -> System Vendor: " IDEALMAX" + But given that the System Vendor is "unstable" for the other + H34 entry, we put NULL in the dmi_system_vendor field. */ + { 0x04f2, 0xb16b, 0, "To be filled by O.E.M.", "H34", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + NULL, "H34" }, + { 0x04f2, 0xb186, 0, "FUJITSU", "FJNB206", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU SIEMENS", "LifeBook T4310" }, + { 0x04f2, 0xb186, 0, "FUJITSU", "FJNB206", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LifeBook T4310" }, + { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB11", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK PH521" }, + { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB13", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK SH531" }, + { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB16", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK LH531" }, + { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB18", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "FMVN77ED" }, + { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB19", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "FUJITSU", "LIFEBOOK NH751" }, + { 0x04f2, 0xb217, 0, "LENOVO", "42982YG", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb217, 0, "LENOVO", "42992QG", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x04f2, 0xb27c, 0, "LENOVO", "12973MG", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "ThinkPad Edge E325" }, + { 0x064e, 0xa111, 0, "Acer, Inc.", "Prespa1 ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "Acer, inc.", "Aspire 5570 " }, + /* 2 reports: + Unknown laptop model -> System Vendor: " IDEALMAX" + Síragon SL-6120 -> System Vendor: "PEGA PC" + So we just put NULL in the dmi_system_vendor field. */ + { 0x064e, 0xa116, 0, "To be filled by O.E.M.", "H34", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + NULL, "H34" }, + { 0x064e, 0xa212, 0, "MEDIONAG", "WeTab ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + { 0x174f, 0x6a51, 0, NULL, "S96S", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, + "MicroLink", "S96S" }, + { 0x17ef, 0x480c, 0, "LENOVO", "7449C8G", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "ThinkPad X200 Tablet" }, + /* Disabled due to http://bugs.debian.org/667958 + { 0x17ef, 0x4816, 0, "LENOVO", "0831CTO", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "ThinkPad X201 Tablet" }, */ + { 0x5986, 0x0200, 0, "LENOVO", "SPEEDY ", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "Lenovo IdeaPad Y510" }, + { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "Lenovo IdeaPad U330" }, + { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL, + "Lenovo IdeaPad Y330" }, + { 0xeb1a, 0x2750, 0, "CLEVO", "D900K", + V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED }, + + /* Second: devices which should use some software processing by default */ + /* jl2005bcd devices */ + { 0x0979, 0x0227, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* sn9c101 / sn9c102 based devices (sonixb) */ + { 0x0c45, 0x6011, 0, NULL, NULL, 0, 1500 }, /* OV6650, no WB needed */ + { 0x0c45, 0x6019, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */ + { 0x0c45, 0x608f, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */ + { 0x0c45, 0x60b0, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */ + { 0x0c45, 0x6000, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, /* other */ + { 0x0c45, 0x6020, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, /* other */ + { 0x0c45, 0x60af, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, /* PAS202 */ + /* sn9c105 / sn9c120 based devices (sonixj) */ + { 0x0c45, 0x60fe, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */ + { 0x0c45, 0x610e, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */ + { 0x0c45, 0x6128, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OM6802 */ + { 0x0c45, 0x612e, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */ + { 0x0c45, 0x613e, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */ + /* Pac207 based devices */ + { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x145f, 0x013a, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + { 0x2001, 0xf115, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, + /* Pac7302 based devices */ + { 0x093a, 0x2620, 0x0f, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 }, + { 0x06f8, 0x3009, 0, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 }, + { 0x06f8, 0x301b, 0, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 }, + { 0x145f, 0x013c, 0, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 }, + { 0x1ae7, 0x2001, 0, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 }, + /* Pac7311 based devices */ + { 0x093a, 0x2600, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* sq905 devices */ + { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* spca561 revison 12a devices */ + { 0x041e, 0x403b, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x046d, 0x0928, 7, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + /* logitech quickcam express stv06xx 2 versions: + pb0100 only needs whitebalance, see software autogain code enable below + hdcs10xx needs both whitebalance and autogain. */ + { 0x046d, 0x0840, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + /* logitech quickcam messenger variants, st6422 */ + { 0x046d, 0x08f0, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08f5, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08f6, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + { 0x046d, 0x08da, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN }, + /* mr97310a cams, note some models do not have the necessary controls, for + those we will only do whitebal. see software autogain code enable below */ + { 0x08ca, 0x0111, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x093a, 0x010e, 1, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + /* stv0680 based cams */ + { 0x0553, 0x0202, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + { 0x041e, 0x4007, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* vicam based cams */ + { 0x04c1, 0x009d, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN, 1500 }, + { 0x0602, 0x1001, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN, 1500 }, + /* se401 cams, mirrored and need wb + autogain */ + { 0x03e8, 0x0004, 0, NULL, NULL, + V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x0471, 0x030b, 0, NULL, NULL, + V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x047d, 0x5001, 0, NULL, NULL, + V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x047d, 0x5002, 0, NULL, NULL, + V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x047d, 0x5003, 0, NULL, NULL, + V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN }, + /* W996xCF based cams, must use jpeglite because of funky JPEG fmt */ + { 0x041e, 0x4003, 0, NULL, NULL, V4LCONTROL_FORCE_TINYJPEG }, + { 0x1046, 0x9967, 0, NULL, NULL, V4LCONTROL_FORCE_TINYJPEG }, +}; + +static const struct v4l2_queryctrl fake_controls[]; + +static const char *asus_board_vendor[] = { + "ASUSTeK Computer Inc.", + "ASUSTeK Computer INC.", + "ASUS CORPORATION", + "PEGATRON CORPORATION", + NULL }; + +static const char *asus_board_name[] = { + "A3[A-Z]*", "A6R*", "A7J", "A7M", "A7Sn", + "B50[A-Z]*", + "F[23579][A-Z]*", "F70[A-Z]*", "F[58]2[A-Z]*", + "G[12][A-Z]*", "G[57]0[A-Z]*", + "K[4567]0[A-Z]*", "K[56]1[A-Z]*", "K52[A-Z]*", "K[45]3[A-Z]*", + "N[12579]0[A-Z]*", "N[56]1[A-Z]*", "N82[A-Z]*", "N[47]3[A-Z]*", + "P5[02][A-Z]*", "P81[A-Z]*", + "U6[A-Z]*", "U[28]0[A-Z]*", "U3[1356][A-Z]*", "U5[23][A-Z]*", + "UL[35]0[A-Z]*", + "X55[A-Z]*", "X58[A-Z]*", "X71[A-Z]*", + /* special devices */ + "900AX", + "N5051Tp", "NX90Jq", + "T101MT", "T14C", + "VX3", + "W7S*", + NULL }; + +static const struct v4lcontrol_usb_id asus_camera_id[] = { + { 0x04f2, 0xb012 }, + { 0x04f2, 0xb034 }, + { 0x04f2, 0xb036 }, + { 0x04f2, 0xb071 }, + { 0x04f2, 0xb072 }, + { 0x04f2, 0xb106 }, + { 0x04f2, 0xb169 }, + { 0x04f2, 0xb16b }, + { 0x04f2, 0xb1b9 }, + { 0x04f2, 0xb1bb }, + { 0x04f2, 0xb1be }, + { 0x04f2, 0xb1e5 }, + { 0x064e, 0xa111 }, + { 0x064e, 0xa116 }, + { 0x064e, 0xa136 }, + { 0x090c, 0xe370 }, + { 0x13d3, 0x5094 }, + { 0x13d3, 0x5111 }, + { 0x13d3, 0x5120 }, + { 0x13d3, 0x5122 }, + { 0x13d3, 0x5126 }, + { 0x13d3, 0x5130 }, + { 0x13d3, 0x5702 }, + { 0x174f, 0x1120 }, + { 0x174f, 0x1408 }, + { 0x174f, 0x5a35 }, + { 0x174f, 0x8a31 }, + { 0x174f, 0xa311 }, + { 0x1d4d, 0x1002 }, + { 0x05e1, 0x0501 }, + { 0x0000, 0x0000 } +}; + +static const struct v4lcontrol_upside_down_table upside_down[] = { + { asus_board_vendor, asus_board_name, asus_camera_id }, +}; + +static void v4lcontrol_get_dmi_string(const char *sysfs_prefix, const char *string, char *buf, int size) +{ + FILE *f; + char *s, sysfs_name[512]; + + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/dmi/id/%s", sysfs_prefix, string); + f = fopen(sysfs_name, "r"); + if (!f) { + /* Try again with a different sysfs path, not sure if this is needed + but we used to look under /sys/devices/virtual/dmi/id in older + libv4l versions, but this did not work with some kernels */ + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/devices/virtual/dmi/id/%s", sysfs_prefix, string); + f = fopen(sysfs_name, "r"); + if (!f) { + buf[0] = 0; + return; + } + } + + s = fgets(buf, size, f); + if (s) + s[strlen(s) - 1] = 0; + fclose(f); +} + +static int v4lcontrol_get_usb_info(struct v4lcontrol_data *data, + const char *sysfs_prefix, + unsigned short *vendor_id, unsigned short *product_id, + int *speed) +{ + FILE *f; + int i, minor_dev; + struct stat st; + char sysfs_name[512]; + char c, *s, buf[32]; + + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux", sysfs_prefix); + + /* Check for sysfs mounted before trying to search */ + if (stat(sysfs_name, &st) != 0) + return 0; /* Not found, sysfs not mounted? */ + + if (fstat(data->fd, &st) || !S_ISCHR(st.st_mode)) + return 0; /* Should never happen */ + + /* find ourselve in sysfs */ + for (i = 0; i < 256; i++) { + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux/video%d/dev", sysfs_prefix, i); + f = fopen(sysfs_name, "r"); + if (!f) + continue; + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (s && sscanf(buf, "%*d:%d%c", &minor_dev, &c) == 2 && + c == '\n' && minor_dev == (int)minor(st.st_rdev)) + break; + } + if (i == 256) + return 0; /* Not found, sysfs not mounted? */ + + /* Get vendor and product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux/video%d/device/modalias", sysfs_prefix, i); + f = fopen(sysfs_name, "r"); + if (f) { + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "usb:v%4hxp%4hx%c", vendor_id, product_id, + &c) != 3 || c != 'd') + return 0; /* Not an USB device */ + + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux/video%d/device/../speed", sysfs_prefix, i); + } else { + /* Try again assuming the device link points to the usb + device instead of the usb interface (bug in older versions + of gspca) */ + + /* Get vendor ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux/video%d/device/idVendor", sysfs_prefix, i); + f = fopen(sysfs_name, "r"); + if (!f) + return 0; /* Not an USB device (or no sysfs) */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%04hx%c", vendor_id, &c) != 2 || + c != '\n') + return 0; /* Should never happen */ + + /* Get product ID */ + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux/video%d/device/idProduct", sysfs_prefix, i); + f = fopen(sysfs_name, "r"); + if (!f) + return 0; /* Should never happen */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%04hx%c", product_id, &c) != 2 || + c != '\n') + return 0; /* Should never happen */ + + snprintf(sysfs_name, sizeof(sysfs_name), + "%s/sys/class/video4linux/video%d/device/speed", sysfs_prefix, i); + } + + f = fopen(sysfs_name, "r"); + if (!f) + return 0; /* Should never happen */ + + s = fgets(buf, sizeof(buf), f); + fclose(f); + + if (!s || sscanf(s, "%d%c", speed, &c) != 2 || (c != '\n' && c != '.')) + return 0; /* Should never happen */ + + return 1; +} + +/* + * Tries to match value in NULL terminated table_entries string array + * aganist the space trimmed dmi_value. The upside down table entries + * might contain shell wildcard patterns [see glob(7)]. + * + * Returns non zero value if value is found, otherwise 0. + */ +static int find_dmi_string(const char **table_entries, const char *dmi_value) +{ + const char *start = dmi_value; + const char **entry_ptr; + char *trimmed_dmi; + size_t n; + int found = 0; + + if (!start) return 0; + + /* trim value */ + while (isspace(*start)) start++; + n = strlen(start); + while (n > 0 && isspace(start[n-1])) --n; + trimmed_dmi = strndup(start, n); + + /* find trimmed value */ + for (entry_ptr = table_entries; *entry_ptr; entry_ptr++) { + found = fnmatch(*entry_ptr, trimmed_dmi, 0) == 0; + /* fprintf(stderr, "find_dmi_string('%s', '%s'->'%s')=%i\n", *entry_ptr, dmi_value, trimmed_dmi, found); */ + if (found) + break; + } + + free(trimmed_dmi); + + return found; +} + +/* + * Tries to find an USB id in table_entries array + * + * Returns non zero value if value is found, otherwise 0. + */ +static int find_usb_id(const struct v4lcontrol_usb_id *table_entries, + unsigned short vendor_id, unsigned short product_id) +{ + const struct v4lcontrol_usb_id *entry_ptr; + for (entry_ptr = table_entries; entry_ptr->vendor_id && entry_ptr->product_id; ++entry_ptr) + if (entry_ptr->vendor_id == vendor_id && entry_ptr->product_id == product_id) return 1; + return 0; +} + +static void v4lcontrol_get_flags_from_db(struct v4lcontrol_data *data, + const char *sysfs_prefix, + unsigned short vendor_id, unsigned short product_id) +{ + char dmi_system_vendor[512], dmi_system_name[512], dmi_system_version[512]; + char dmi_board_vendor[512], dmi_board_name[512], dmi_board_version[512]; + int i; + + /* Get DMI board and system strings */ + v4lcontrol_get_dmi_string(sysfs_prefix, "sys_vendor", dmi_system_vendor, + sizeof(dmi_system_vendor)); + v4lcontrol_get_dmi_string(sysfs_prefix, "product_name", dmi_system_name, + sizeof(dmi_system_name)); + v4lcontrol_get_dmi_string(sysfs_prefix, "product_version", dmi_system_version, + sizeof(dmi_system_version)); + + v4lcontrol_get_dmi_string(sysfs_prefix, "board_vendor", dmi_board_vendor, + sizeof(dmi_board_vendor)); + v4lcontrol_get_dmi_string(sysfs_prefix, "board_name", dmi_board_name, + sizeof(dmi_board_name)); + v4lcontrol_get_dmi_string(sysfs_prefix, "board_version", dmi_board_version, + sizeof(dmi_board_version)); + + for (i = 0; i < ARRAY_SIZE(v4lcontrol_flags); i++) + if (v4lcontrol_flags[i].vendor_id == vendor_id && + v4lcontrol_flags[i].product_id == + (product_id & ~v4lcontrol_flags[i].product_mask) && + + (v4lcontrol_flags[i].dmi_system_vendor == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_vendor, dmi_system_vendor)) && + (v4lcontrol_flags[i].dmi_system_name == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_name, dmi_system_name)) && + (v4lcontrol_flags[i].dmi_system_version == NULL || + !strcmp(v4lcontrol_flags[i].dmi_system_version, dmi_system_version)) && + + (v4lcontrol_flags[i].dmi_board_vendor == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_vendor, dmi_board_vendor)) && + (v4lcontrol_flags[i].dmi_board_name == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name)) && + (v4lcontrol_flags[i].dmi_board_version == NULL || + !strcmp(v4lcontrol_flags[i].dmi_board_version, dmi_board_version))) { + data->flags |= v4lcontrol_flags[i].flags; + data->flags_info = &v4lcontrol_flags[i]; + /* Entries in the v4lcontrol_flags table override + wildcard matches in the upside_down table. */ + return; + } + + for (i = 0; i < ARRAY_SIZE(upside_down); i++) + if (find_dmi_string(upside_down[i].board_vendor, dmi_board_vendor) && + find_dmi_string(upside_down[i].board_name, dmi_board_name) && + find_usb_id(upside_down[i].camera_id, vendor_id, product_id)) { + /* found entry */ + data->flags |= V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED; + break; + } +} + +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv, + const struct libv4l_dev_ops *dev_ops, int always_needs_conversion) +{ + int shm_fd; + int i, rc, got_usb_info, speed, init = 0; + char *s, shm_name[256], pwd_buf[1024]; + struct v4l2_capability cap; + struct v4l2_queryctrl ctrl; + struct passwd pwd, *pwd_p; + unsigned short vendor_id = 0; + unsigned short product_id = 0; + struct v4l2_input input; + + struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data)); + + if (!data) { + fprintf(stderr, "libv4lcontrol: error: out of memory!\n"); + return NULL; + } + + data->fd = fd; + data->dev_ops = dev_ops; + data->dev_ops_priv = dev_ops_priv; + + /* Check if the driver has indicated some form of flipping is needed */ + if ((data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_G_INPUT, &input.index) == 0) && + (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUMINPUT, &input) == 0)) { + if (input.status & V4L2_IN_ST_HFLIP) + data->flags |= V4LCONTROL_HFLIPPED; + if (input.status & V4L2_IN_ST_VFLIP) + data->flags |= V4LCONTROL_VFLIPPED; + } + + s = getenv("LIBV4LCONTROL_SYSFS_PREFIX"); + if (!s) + s = ""; + + got_usb_info = v4lcontrol_get_usb_info(data, s, &vendor_id, &product_id, + &speed); + if (got_usb_info) { + v4lcontrol_get_flags_from_db(data, s, vendor_id, product_id); + switch (speed) { + case 12: + data->bandwidth = 1023 * 1000; + break; + case 480: + data->bandwidth = 3 * 1024 * 8000; + break; + case 5000: + data->bandwidth = 48 * 1024 * 8000; + break; + default: + /* heuh, low speed device, or ... ? */ + data->bandwidth = speed / 20; + } + } else + data->bandwidth = 0; + + /* Allow overriding through environment */ + s = getenv("LIBV4LCONTROL_FLAGS"); + if (s) + data->flags = strtol(s, NULL, 0); + + ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; + if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCTRL, &ctrl) == 0) + data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL; + + /* If the device always needs conversion, we can add fake controls at no cost + (no cost when not activated by the user that is) */ + if (always_needs_conversion || v4lcontrol_needs_conversion(data)) { + for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) { + ctrl.id = fake_controls[i].id; + rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCTRL, &ctrl); + if (rc == -1 || + (rc == 0 && + (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))) + data->controls |= 1 << i; + } + } + + /* Check if a camera does not have hardware autogain and has the necessary + controls, before enabling sw autogain, even if this is requested by flags. + This is necessary because some cameras share a USB-ID, but can have + different sensors with / without autogain or the necessary controls. */ + while (data->flags & V4LCONTROL_WANTS_AUTOGAIN) { + ctrl.id = V4L2_CID_AUTOGAIN; + rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCTRL, &ctrl); + if (rc == 0 && !(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + break; + + ctrl.id = V4L2_CID_EXPOSURE; + rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCTRL, &ctrl); + if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + break; + + ctrl.id = V4L2_CID_GAIN; + rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCTRL, &ctrl); + if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + break; + + data->controls |= 1 << V4LCONTROL_AUTOGAIN | + 1 << V4LCONTROL_AUTOGAIN_TARGET; + break; + } + + /* Allow overriding through environment */ + s = getenv("LIBV4LCONTROL_CONTROLS"); + if (s) + data->controls = strtol(s, NULL, 0); + + if (data->controls == 0) + return data; /* No need to create a shared memory segment */ + + if (data->dev_ops->ioctl(data->dev_ops_priv, fd, + VIDIOC_QUERYCAP, &cap)) { + perror("libv4lcontrol: error querying device capabilities"); + goto error; + } + + if (getpwuid_r(geteuid(), &pwd, pwd_buf, sizeof(pwd_buf), &pwd_p) == 0) { + if (got_usb_info) + snprintf(shm_name, 256, "/libv4l-%s:%s:%04x:%04x:%s", pwd.pw_name, + cap.bus_info, (int)vendor_id, (int)product_id, cap.card); + else + snprintf(shm_name, 256, "/libv4l-%s:%s:%s", pwd.pw_name, + cap.bus_info, cap.card); + } else { + perror("libv4lcontrol: error getting username using uid instead"); + if (got_usb_info) + snprintf(shm_name, 256, "/libv4l-%lu:%s:%04x:%04x:%s", + (unsigned long)geteuid(), cap.bus_info, + (int)vendor_id, (int)product_id, cap.card); + else + snprintf(shm_name, 256, "/libv4l-%lu:%s:%s", (unsigned long)geteuid(), + cap.bus_info, cap.card); + } + + /* / is not allowed inside shm names */ + for (i = 1; shm_name[i]; i++) + if (shm_name[i] == '/') + shm_name[i] = '-'; + +#ifndef ANDROID + /* Open the shared memory object identified by shm_name */ + shm_fd = shm_open(shm_name, (O_CREAT | O_EXCL | O_RDWR), (S_IREAD | S_IWRITE)); + if (shm_fd >= 0) + init = 1; + else + shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE)); + + if (shm_fd >= 0) { + /* Set the shared memory size */ + ftruncate(shm_fd, V4LCONTROL_SHM_SIZE); + + /* Retreive a pointer to the shm object */ + data->shm_values = mmap(NULL, V4LCONTROL_SHM_SIZE, (PROT_READ | PROT_WRITE), + MAP_SHARED, shm_fd, 0); + close(shm_fd); + + if (data->shm_values == MAP_FAILED) { + perror("libv4lcontrol: error shm mmap failed"); + data->shm_values = NULL; + } + } else + perror("libv4lcontrol: error creating shm segment failed"); +#endif + + /* Fall back to malloc */ + if (data->shm_values == NULL) { + fprintf(stderr, + "libv4lcontrol: falling back to malloc-ed memory for controls\n"); + data->shm_values = malloc(V4LCONTROL_SHM_SIZE); + if (!data->shm_values) { + fprintf(stderr, "libv4lcontrol: error: out of memory!\n"); + goto error; + } + init = 1; + data->priv_flags |= V4LCONTROL_MEMORY_IS_MALLOCED; + } + + if (init) { + /* Initialize the new shm object we created */ + memset(data->shm_values, 0, V4LCONTROL_SHM_SIZE); + + for (i = 0; i < V4LCONTROL_COUNT; i++) + data->shm_values[i] = fake_controls[i].default_value; + + if (data->flags & V4LCONTROL_WANTS_WB) + data->shm_values[V4LCONTROL_WHITEBALANCE] = 1; + + if (data->flags_info && data->flags_info->default_gamma) + data->shm_values[V4LCONTROL_GAMMA] = data->flags_info->default_gamma; + } + + return data; + +error: + free(data); + return NULL; +} + +void v4lcontrol_destroy(struct v4lcontrol_data *data) +{ + if (data->controls) { + if (data->priv_flags & V4LCONTROL_MEMORY_IS_MALLOCED) + free(data->shm_values); + else + munmap(data->shm_values, V4LCONTROL_SHM_SIZE); + } + free(data); +} + +static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "White Balance, Automatic", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0 + }, { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 500, /* == 0.5 */ + .maximum = 3000, /* == 3.0 */ + .step = 1, + .default_value = 1000, /* == 1.0 */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, { /* Dummy place holder for V4LCONTROL_AUTO_ENABLE_COUNT */ + }, { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Gain, Automatic", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0 + }, { + .id = V4L2_CTRL_CLASS_USER + 0x2000, /* FIXME */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Gain Target", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 100, + .flags = V4L2_CTRL_FLAG_SLIDER + }, +}; + +static void v4lcontrol_copy_queryctrl(struct v4lcontrol_data *data, + struct v4l2_queryctrl *ctrl, int i) +{ + memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl)); + + /* Hmm, not pretty */ + if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE && + (data->flags & V4LCONTROL_WANTS_WB)) + ctrl->default_value = 1; + + if (ctrl->id == V4L2_CID_GAMMA && data->flags_info && + data->flags_info->default_gamma) + ctrl->default_value = data->flags_info->default_gamma; +} + +int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_queryctrl *ctrl = arg; + int retval; + uint32_t orig_id = ctrl->id; + + /* if we have an exact match return it */ + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + v4lcontrol_copy_queryctrl(data, ctrl, i); + return 0; + } + + /* find out what the kernel driver would respond. */ + retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCTRL, arg); + + if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) && + (orig_id & V4L2_CTRL_FLAG_NEXT_CTRL)) { + /* If the hardware has no more controls check if we still have any + fake controls with a higher id then the hardware's highest */ + if (retval) + ctrl->id = V4L2_CTRL_ID_MASK; + + /* If any of our controls have an id > orig_id but less than + ctrl->id then return that control instead. Note we do not + break when we have a match, but keep iterating, so that + we end up with the fake ctrl with the lowest CID > orig_id. */ + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + (fake_controls[i].id > (orig_id & ~V4L2_CTRL_FLAG_NEXT_CTRL)) && + (fake_controls[i].id <= ctrl->id)) { + v4lcontrol_copy_queryctrl(data, ctrl, i); + retval = 0; + } + } + + return retval; +} + +int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_control *ctrl = arg; + + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + ctrl->value = data->shm_values[i]; + return 0; + } + + return data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_G_CTRL, arg); +} + +static void v4lcontrol_alloc_valid_controls(struct v4lcontrol_data *data, + const struct v4l2_ext_controls *src, + struct v4l2_ext_controls *dst) +{ + struct v4l2_ext_control *ctrl; + unsigned i, j; + + *dst = *src; + if (data->controls == 0) + return; + ctrl = malloc(src->count * sizeof(*ctrl)); + if (ctrl == NULL) + return; + dst->controls = ctrl; + dst->count = 0; + for (i = 0; i < src->count; i++) { + for (j = 0; j < V4LCONTROL_COUNT; j++) + if ((data->controls & (1 << j)) && + src->controls[i].id == fake_controls[j].id) + break; + if (j == V4LCONTROL_COUNT) + ctrl[dst->count++] = src->controls[i]; + } +} + +static void v4lcontrol_free_valid_controls(struct v4lcontrol_data *data, + struct v4l2_ext_controls *src, + struct v4l2_ext_controls *dst) +{ + unsigned i, j, k = 0; + int inc_idx; + + src->error_idx = dst->error_idx; + if (dst->controls == src->controls) + return; + + inc_idx = dst->error_idx < dst->count; + for (i = 0; i < src->count; i++) { + for (j = 0; j < V4LCONTROL_COUNT; j++) + if ((data->controls & (1 << j)) && + src->controls[i].id == fake_controls[j].id) + break; + if (j == V4LCONTROL_COUNT) + src->controls[i] = dst->controls[k++]; + else if (inc_idx) + src->error_idx++; + } + free(dst->controls); +} + +int v4lcontrol_vidioc_g_ext_ctrls(struct v4lcontrol_data *data, void *arg) +{ + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_controls dst; + int i, j; + int res; + + v4lcontrol_alloc_valid_controls(data, ctrls, &dst); + res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_G_EXT_CTRLS, &dst); + v4lcontrol_free_valid_controls(data, ctrls, &dst); + if (res) + return res; + + for (i = 0; (unsigned)i < ctrls->count; i++) { + for (j = 0; j < V4LCONTROL_COUNT; j++) + if ((data->controls & (1 << j)) && + ctrls->controls[i].id == fake_controls[j].id) { + ctrls->controls[i].value = data->shm_values[j]; + break; + } + } + return 0; +} + +int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg) +{ + int i; + struct v4l2_control *ctrl = arg; + + for (i = 0; i < V4LCONTROL_COUNT; i++) + if ((data->controls & (1 << i)) && + ctrl->id == fake_controls[i].id) { + if (ctrl->value > fake_controls[i].maximum || + ctrl->value < fake_controls[i].minimum) { + errno = EINVAL; + return -1; + } + + data->shm_values[i] = ctrl->value; + return 0; + } + + return data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_S_CTRL, arg); +} + +static int v4lcontrol_validate_ext_ctrls(struct v4lcontrol_data *data, + struct v4l2_ext_controls *ctrls) +{ + int i, j; + + if (data->controls == 0) + return 0; + for (i = 0; (unsigned)i < ctrls->count; i++) { + for (j = 0; j < V4LCONTROL_COUNT; j++) + if ((data->controls & (1 << j)) && + ctrls->controls[i].id == fake_controls[j].id) { + if (ctrls->controls[i].value > fake_controls[j].maximum || + ctrls->controls[i].value < fake_controls[j].minimum) { + ctrls->error_idx = i; + errno = EINVAL; + return -1; + } + } + } + return 0; +} + +int v4lcontrol_vidioc_try_ext_ctrls(struct v4lcontrol_data *data, void *arg) +{ + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_controls dst; + int res = v4lcontrol_validate_ext_ctrls(data, ctrls); + + if (res) + return res; + + v4lcontrol_alloc_valid_controls(data, ctrls, &dst); + res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_TRY_EXT_CTRLS, &dst); + v4lcontrol_free_valid_controls(data, ctrls, &dst); + return res; +} + +int v4lcontrol_vidioc_s_ext_ctrls(struct v4lcontrol_data *data, void *arg) +{ + struct v4l2_ext_controls *ctrls = arg; + struct v4l2_ext_controls dst; + int i, j; + int res = v4lcontrol_validate_ext_ctrls(data, ctrls); + + if (res) + return res; + + v4lcontrol_alloc_valid_controls(data, ctrls, &dst); + res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_S_EXT_CTRLS, &dst); + v4lcontrol_free_valid_controls(data, ctrls, &dst); + if (res) + return res; + + for (i = 0; (unsigned)i < ctrls->count; i++) { + for (j = 0; j < V4LCONTROL_COUNT; j++) + if ((data->controls & (1 << j)) && + ctrls->controls[i].id == fake_controls[j].id) { + data->shm_values[j] = ctrls->controls[i].value; + break; + } + } + return 0; +} + +int v4lcontrol_get_bandwidth(struct v4lcontrol_data *data) +{ + return data->bandwidth; +} + +int v4lcontrol_get_flags(struct v4lcontrol_data *data) +{ + return data->flags; +} + +int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl) +{ + if (data->controls & (1 << ctrl)) { + /* Special case for devices with flipped input */ + if ((ctrl == V4LCONTROL_HFLIP && (data->flags & V4LCONTROL_HFLIPPED)) || + (ctrl == V4LCONTROL_VFLIP && (data->flags & V4LCONTROL_VFLIPPED))) + return !data->shm_values[ctrl]; + + return data->shm_values[ctrl]; + } + + return 0; +} + +int v4lcontrol_controls_changed(struct v4lcontrol_data *data) +{ + int res; + + if (!data->controls) + return 0; + + res = memcmp(data->shm_values, data->old_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + memcpy(data->old_values, data->shm_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + return res; +} + +/* See the comment about this in libv4lconvert.h */ +int v4lcontrol_needs_conversion(struct v4lcontrol_data *data) +{ + return data->flags || data->controls; +} diff --git a/libv4lconvert/control/libv4lcontrol.h b/libv4lconvert/control/libv4lcontrol.h new file mode 100644 index 0000000..c386758 --- /dev/null +++ b/libv4lconvert/control/libv4lcontrol.h @@ -0,0 +1,78 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Radjnies Bhansingh +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifndef __LIBV4LCONTROL_H +#define __LIBV4LCONTROL_H + +#include "libv4l-plugin.h" + +/* Flags */ +#define V4LCONTROL_HFLIPPED 0x01 +#define V4LCONTROL_VFLIPPED 0x02 +#define V4LCONTROL_ROTATED_90_JPEG 0x04 +#define V4LCONTROL_WANTS_WB 0x08 +#define V4LCONTROL_WANTS_AUTOGAIN 0x10 +#define V4LCONTROL_FORCE_TINYJPEG 0x20 + +/* Masks */ +#define V4LCONTROL_WANTS_WB_AUTOGAIN (V4LCONTROL_WANTS_WB | V4LCONTROL_WANTS_AUTOGAIN) + +/* Controls */ +enum { + V4LCONTROL_WHITEBALANCE, + V4LCONTROL_HFLIP, + V4LCONTROL_VFLIP, + V4LCONTROL_GAMMA, + /* All fake controls above here are auto enabled when not present in hw */ + V4LCONTROL_AUTO_ENABLE_COUNT, + V4LCONTROL_AUTOGAIN, + V4LCONTROL_AUTOGAIN_TARGET, + V4LCONTROL_COUNT +}; + +struct v4lcontrol_data; + +struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv, + const struct libv4l_dev_ops *dev_ops, int always_needs_conversion); +void v4lcontrol_destroy(struct v4lcontrol_data *data); + +int v4lcontrol_get_bandwidth(struct v4lcontrol_data *data); + +/* Functions used by v4lprocessing to get the control state */ +int v4lcontrol_get_flags(struct v4lcontrol_data *data); +int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl); +/* Check if the controls have changed since the last time this function + was called */ +int v4lcontrol_controls_changed(struct v4lcontrol_data *data); +/* Check if we must go through the conversion path (and thus alloc conversion + buffers, etc. in libv4l2). Note this always return 1 if we *may* need + rotate90 / flipping / processing, as if we actually need this may change + on the fly while the stream is active. */ +int v4lcontrol_needs_conversion(struct v4lcontrol_data *data); + +/* Functions used by v4lconvert to pass vidioc calls from libv4l2 */ +int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_g_ext_ctrls(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_try_ext_ctrls(struct v4lcontrol_data *data, void *arg); +int v4lcontrol_vidioc_s_ext_ctrls(struct v4lcontrol_data *data, void *arg); + +#endif diff --git a/libv4lconvert/cpia1.c b/libv4lconvert/cpia1.c new file mode 100644 index 0000000..25e2741 --- /dev/null +++ b/libv4lconvert/cpia1.c @@ -0,0 +1,213 @@ +/* +# (C) 2010 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include "libv4lconvert-priv.h" +#include +#include + +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* CPIA YUYV (sometimes sort of compressed) */ +int v4lconvert_cpia1_to_yuv420(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height, int yvu) +{ + int x, y, ll, compressed; + unsigned char *udest, *vdest; + + if (width > 352 || height > 288) { + fprintf(stderr, "FATAL ERROR CPIA1 size > 352x288, please report!\n"); + return -1; + } + + if (data->previous_frame == NULL) { + data->previous_frame = malloc(352 * 288 * 3 / 2); + if (data->previous_frame == NULL) { + fprintf(stderr, "cpia1 decode error: could not allocate buffer!\n"); + return -1; + } + } + + if (yvu) { + vdest = dest + width * height; + udest = vdest + width * height / 4; + } else { + udest = dest + width * height; + vdest = udest + width * height / 4; + } + + /* Verify header */ + if (src_size < FRAME_HEADER_SIZE || + src[0] != MAGIC_0 || src[1] != MAGIC_1 || + src[17] != SUBSAMPLE_420 || + src[18] != YUVORDER_YUYV || + (src[25] - src[24]) * 8 != width || + (src[27] - src[26]) * 4 != height || + (src[28] != NOT_COMPRESSED && src[28] != COMPRESSED) || + (src[29] != NO_DECIMATION && src[29] != DECIMATION_ENAB)) { + fprintf(stderr, "cpia1 decode error: invalid header\n"); + return -1; + } + + if (src[29] == DECIMATION_ENAB) { + fprintf(stderr, "cpia1 decode error: decimation is not supported\n"); + return -1; + } + + compressed = src[28] == COMPRESSED; + + src += FRAME_HEADER_SIZE; + src_size -= FRAME_HEADER_SIZE; + + if (!compressed) { + for (y = 0; y < height && src_size > 2; y++) { + ll = src[0] | (src[1] << 8); + src += 2; + src_size -= 2; + if (src_size < ll) { + fprintf(stderr, "cpia1 decode error: short frame\n"); + return -1; + } + if (src[ll - 1] != EOL) { + fprintf(stderr, "cpia1 decode error: invalid terminated line\n"); + return -1; + } + + if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */ + if (ll != 2 * width + 1) { + fprintf(stderr, "cpia1 decode error: invalid uncompressed even ll\n"); + return -1; + } + + /* copy the Y values */ + for (x = 0; x < width; x += 2) { + *dest++ = src[0]; + *dest++ = src[2]; + src += 4; + } + + /* copy the UV values */ + src -= 2 * width; + for (x = 0; x < width; x += 2) { + *udest++ = src[1]; + *vdest++ = src[3]; + src += 4; + } + } else { /* Odd line only Y values */ + if (ll != width + 1) { + fprintf(stderr, "cpia1 decode error: invalid uncompressed odd ll\n"); + return -1; + } + + memcpy(dest, src, width); + dest += width; + src += width; + } + src++; /* Skip EOL */ + src_size -= ll; + } + } else { /* compressed */ + /* Pre-fill dest with previous frame, as the cpia1 "compression" consists + of simply ommitting certain pixels */ + memcpy(dest, data->previous_frame, width * height * 3 / 2); + + for (y = 0; y < height && src_size > 2; y++) { + ll = src[0] | (src[1] << 8); + src += 2; + src_size -= 2; + if (src_size < ll) { + fprintf(stderr, "cpia1 decode error: short frame\n"); + return -1; + } + if (src[ll - 1] != EOL) { + fprintf(stderr, "cpia1 decode error: invalid terminated line\n"); + return -1; + } + + /* Do this now as we use ll as loop variable below */ + src_size -= ll; + for (x = 0; x < width && ll > 1; ) { + if (*src & 1) { /* skip N pixels */ + int skip = *src >> 1; + + if (skip & 1) { + fprintf(stderr, "cpia1 decode error: odd number of pixels to skip"); + return -1; + } + + if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */ + dest += skip; + udest += skip / 2; + vdest += skip / 2; + } else { /* Odd line only Y values */ + dest += skip; + } + x += skip; + src++; + ll--; + } else { + if (!(y & 1)) { /* Even line Y + UV in the form of YUYV */ + *dest++ = *src++; + *udest++ = *src++; + *dest++ = *src++; + *vdest++ = *src++; + ll -= 4; + } else { /* Odd line only Y values */ + *dest++ = *src++; + *dest++ = *src++; + ll -= 2; + } + x += 2; + } + } + if (ll != 1 || x != width) { + fprintf(stderr, "cpia1 decode error: line length mismatch\n"); + return -1; + } + src++; /* Skip EOL */ + } + } + + if (y != height) { + fprintf(stderr, "cpia1 decode error: frame height mismatch\n"); + return -1; + } + + if (src_size < 4 || src[src_size - 4] != EOI || src[src_size - 3] != EOI || + src[src_size - 2] != EOI || src[src_size - 1] != EOI) { + fprintf(stderr, "cpia1 decode error: invaled EOI marker\n"); + return -1; + } + + /* Safe frame for decompression of the next frame */ + dest -= width * height; + memcpy(data->previous_frame, dest, width * height * 3 / 2); + + return 0; +} diff --git a/libv4lconvert/crop.c b/libv4lconvert/crop.c new file mode 100644 index 0000000..21ebadb --- /dev/null +++ b/libv4lconvert/crop.c @@ -0,0 +1,287 @@ +/* + +# RGB and YUV crop routines + +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + + */ + +#include +#include "libv4lconvert-priv.h" + + +static void v4lconvert_reduceandcrop_rgbbgr24( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + unsigned int x, y; + int startx = src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width; + int starty = src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height; + + src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx; + + for (y = 0; y < dest_fmt->fmt.pix.height; y++) { + unsigned char *mysrc = src; + for (x = 0; x < dest_fmt->fmt.pix.width; x++) { + *(dest++) = *(mysrc++); + *(dest++) = *(mysrc++); + *(dest++) = *(mysrc++); + mysrc += 3; /* skip one pixel */ + } + src += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */ + } +} + +static void v4lconvert_crop_rgbbgr24(unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + unsigned int x; + int startx = (src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2; + int starty = (src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2; + + src += starty * src_fmt->fmt.pix.bytesperline + 3 * startx; + + for (x = 0; x < dest_fmt->fmt.pix.height; x++) { + memcpy(dest, src, dest_fmt->fmt.pix.width * 3); + src += src_fmt->fmt.pix.bytesperline; + dest += dest_fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_reduceandcrop_yuv420( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + unsigned int x, y; + unsigned int dest_height_half = dest_fmt->fmt.pix.height / 2; + unsigned int dest_width_half = dest_fmt->fmt.pix.width / 2; + int startx = (src_fmt->fmt.pix.width / 2 - dest_fmt->fmt.pix.width) & ~1; + int starty = (src_fmt->fmt.pix.height / 2 - dest_fmt->fmt.pix.height) & ~1; + unsigned char *mysrc, *mysrc2; + + /* Y */ + mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx; + for (y = 0; y < dest_fmt->fmt.pix.height; y++) { + mysrc2 = mysrc; + for (x = 0; x < dest_fmt->fmt.pix.width; x++) { + *(dest++) = *mysrc2; + mysrc2 += 2; /* skip one pixel */ + } + mysrc += 2 * src_fmt->fmt.pix.bytesperline; /* skip one line */ + } + + /* U */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (y = 0; y < dest_height_half; y++) { + mysrc2 = mysrc; + for (x = 0; x < dest_width_half; x++) { + *(dest++) = *mysrc2; + mysrc2 += 2; /* skip one pixel */ + } + mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */ + } + + /* V */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4 + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (y = 0; y < dest_height_half; y++) { + mysrc2 = mysrc; + for (x = 0; x < dest_width_half; x++) { + *(dest++) = *mysrc2; + mysrc2 += 2; /* skip one pixel */ + } + mysrc += src_fmt->fmt.pix.bytesperline ; /* skip one line */ + } +} + +static void v4lconvert_crop_yuv420(unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + unsigned int x; + int startx = ((src_fmt->fmt.pix.width - dest_fmt->fmt.pix.width) / 2) & ~1; + int starty = ((src_fmt->fmt.pix.height - dest_fmt->fmt.pix.height) / 2) & ~1; + unsigned char *mysrc = src + starty * src_fmt->fmt.pix.bytesperline + startx; + + /* Y */ + for (x = 0; x < dest_fmt->fmt.pix.height; x++) { + memcpy(dest, mysrc, dest_fmt->fmt.pix.width); + mysrc += src_fmt->fmt.pix.bytesperline; + dest += dest_fmt->fmt.pix.bytesperline; + } + + /* U */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) { + memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2); + mysrc += src_fmt->fmt.pix.bytesperline / 2; + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + /* V */ + mysrc = src + src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline * 5 / 4 + + (starty / 2) * src_fmt->fmt.pix.bytesperline / 2 + startx / 2; + for (x = 0; x < dest_fmt->fmt.pix.height / 2; x++) { + memcpy(dest, mysrc, dest_fmt->fmt.pix.width / 2); + mysrc += src_fmt->fmt.pix.bytesperline / 2; + dest += dest_fmt->fmt.pix.bytesperline / 2; + } +} + +/* Ok, so this is not really cropping, but more the reverse, whatever */ +static void v4lconvert_add_border_rgbbgr24( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int y; + int borderx = (dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2; + int bordery = (dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2; + + for (y = 0; y < bordery; y++) { + memset(dest, 0, dest_fmt->fmt.pix.width * 3); + dest += dest_fmt->fmt.pix.bytesperline; + } + + for (y = 0; (unsigned)y < src_fmt->fmt.pix.height; y++) { + memset(dest, 0, borderx * 3); + dest += borderx * 3; + + memcpy(dest, src, src_fmt->fmt.pix.width * 3); + src += src_fmt->fmt.pix.bytesperline; + dest += src_fmt->fmt.pix.width * 3; + + memset(dest, 0, borderx * 3); + dest += dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width) * 3; + } + + for (y = 0; y < bordery; y++) { + memset(dest, 0, dest_fmt->fmt.pix.width * 3); + dest += dest_fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_add_border_yuv420( + unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + int y; + int borderx = ((dest_fmt->fmt.pix.width - src_fmt->fmt.pix.width) / 2) & ~1; + int bordery = ((dest_fmt->fmt.pix.height - src_fmt->fmt.pix.height) / 2) & ~1; + + /* Y */ + for (y = 0; y < bordery; y++) { + memset(dest, 16, dest_fmt->fmt.pix.width); + dest += dest_fmt->fmt.pix.bytesperline; + } + + for (y = 0; (unsigned)y < src_fmt->fmt.pix.height; y++) { + memset(dest, 16, borderx); + dest += borderx; + + memcpy(dest, src, src_fmt->fmt.pix.width); + src += src_fmt->fmt.pix.bytesperline; + dest += src_fmt->fmt.pix.width; + + memset(dest, 16, borderx); + dest += dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width); + } + + for (y = 0; y < bordery; y++) { + memset(dest, 16, dest_fmt->fmt.pix.width); + dest += dest_fmt->fmt.pix.bytesperline; + } + + /* U */ + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + for (y = 0; (unsigned)y < src_fmt->fmt.pix.height / 2; y++) { + memset(dest, 128, borderx / 2); + dest += borderx / 2; + + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + src += src_fmt->fmt.pix.bytesperline / 2; + dest += src_fmt->fmt.pix.width / 2; + + memset(dest, 128, borderx / 2); + dest += (dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width)) / 2; + } + + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + /* V */ + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } + + for (y = 0; (unsigned)y < src_fmt->fmt.pix.height / 2; y++) { + memset(dest, 128, borderx / 2); + dest += borderx / 2; + + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + src += src_fmt->fmt.pix.bytesperline / 2; + dest += src_fmt->fmt.pix.width / 2; + + memset(dest, 128, borderx / 2); + dest += (dest_fmt->fmt.pix.bytesperline - + (borderx + src_fmt->fmt.pix.width)) / 2; + } + + for (y = 0; y < bordery / 2; y++) { + memset(dest, 128, dest_fmt->fmt.pix.width / 2); + dest += dest_fmt->fmt.pix.bytesperline / 2; + } +} + +void v4lconvert_crop(unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt) +{ + switch (dest_fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height) + v4lconvert_add_border_rgbbgr24(src, dest, src_fmt, dest_fmt); + else if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height) + v4lconvert_reduceandcrop_rgbbgr24(src, dest, src_fmt, dest_fmt); + else + v4lconvert_crop_rgbbgr24(src, dest, src_fmt, dest_fmt); + break; + + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + if (src_fmt->fmt.pix.width <= dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height <= dest_fmt->fmt.pix.height) + v4lconvert_add_border_yuv420(src, dest, src_fmt, dest_fmt); + else if (src_fmt->fmt.pix.width >= 2 * dest_fmt->fmt.pix.width && + src_fmt->fmt.pix.height >= 2 * dest_fmt->fmt.pix.height) + v4lconvert_reduceandcrop_yuv420(src, dest, src_fmt, dest_fmt); + else + v4lconvert_crop_yuv420(src, dest, src_fmt, dest_fmt); + break; + } +} diff --git a/libv4lconvert/flip.c b/libv4lconvert/flip.c new file mode 100644 index 0000000..d9aa863 --- /dev/null +++ b/libv4lconvert/flip.c @@ -0,0 +1,266 @@ +/* + +# RGB / YUV flip/rotate routines + +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + + */ + +#include +#include "libv4lconvert-priv.h" + +static void v4lconvert_vflip_rgbbgr24(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + unsigned int y; + + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline; + for (y = 0; y < fmt->fmt.pix.height; y++) { + src -= fmt->fmt.pix.bytesperline; + memcpy(dest, src, fmt->fmt.pix.width * 3); + dest += fmt->fmt.pix.width * 3; + } +} + +static void v4lconvert_vflip_yuv420(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + unsigned int y; + + /* First flip the Y plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline; + for (y = 0; y < fmt->fmt.pix.height; y++) { + src -= fmt->fmt.pix.bytesperline; + memcpy(dest, src, fmt->fmt.pix.width); + dest += fmt->fmt.pix.width; + } + + /* Now flip the U plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline * 5 / 4; + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src -= fmt->fmt.pix.bytesperline / 2; + memcpy(dest, src, fmt->fmt.pix.width / 2); + dest += fmt->fmt.pix.width / 2; + } + + /* Last flip the V plane */ + src += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 2; + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src -= fmt->fmt.pix.bytesperline / 2; + memcpy(dest, src, fmt->fmt.pix.width / 2); + dest += fmt->fmt.pix.width / 2; + } +} + +static void v4lconvert_hflip_rgbbgr24(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + unsigned int x, y; + + for (y = 0; y < fmt->fmt.pix.height; y++) { + src += fmt->fmt.pix.width * 3; + for (x = 0; x < fmt->fmt.pix.width; x++) { + src -= 3; + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest += 3; + } + src += fmt->fmt.pix.bytesperline; + } +} + +static void v4lconvert_hflip_yuv420(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + unsigned int x, y; + + /* First flip the Y plane */ + for (y = 0; y < fmt->fmt.pix.height; y++) { + src += fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline; + } + + /* Now flip the U plane */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src += fmt->fmt.pix.width / 2; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline / 2; + } + + /* Last flip the V plane */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + src += fmt->fmt.pix.width / 2; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) + *dest++ = *--src; + src += fmt->fmt.pix.bytesperline / 2; + } +} + +static void v4lconvert_rotate180_rgbbgr24(const unsigned char *src, + unsigned char *dst, int width, int height) +{ + int i; + + src += 3 * width * height - 3; + + for (i = 0; i < width * height; i++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + src -= 3; + } +} + +static void v4lconvert_rotate180_yuv420(const unsigned char *src, + unsigned char *dst, int width, int height) +{ + int i; + + /* First flip x and y of the Y plane */ + src += width * height - 1; + for (i = 0; i < width * height; i++) + *dst++ = *src--; + + /* Now flip the U plane */ + src += width * height * 5 / 4; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; + + /* Last flip the V plane */ + src += width * height / 2; + for (i = 0; i < width * height / 4; i++) + *dst++ = *src--; +} + +static void v4lconvert_rotate90_rgbbgr24(const unsigned char *src, + unsigned char *dst, int destwidth, int destheight) +{ + int x, y; +#define srcwidth destheight +#define srcheight destwidth + + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = ((srcheight - x - 1) * srcwidth + y) * 3; + *dst++ = src[offset++]; + *dst++ = src[offset++]; + *dst++ = src[offset]; + } +} + +static void v4lconvert_rotate90_yuv420(const unsigned char *src, + unsigned char *dst, int destwidth, int destheight) +{ + int x, y; + + /* Y-plane */ + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* U-plane */ + src += srcwidth * srcheight; + destwidth /= 2; + destheight /= 2; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } + + /* V-plane */ + src += srcwidth * srcheight; + for (y = 0; y < destheight; y++) + for (x = 0; x < destwidth; x++) { + int offset = (srcheight - x - 1) * srcwidth + y; + *dst++ = src[offset]; + } +} + +void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt) +{ + int tmp; + + tmp = fmt->fmt.pix.width; + fmt->fmt.pix.width = fmt->fmt.pix.height; + fmt->fmt.pix.height = tmp; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate90_rgbbgr24(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_rotate90_yuv420(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + } + v4lconvert_fixup_fmt(fmt); +} + +void v4lconvert_flip(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt, int hflip, int vflip) +{ + if (vflip && hflip) { + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_rotate180_rgbbgr24(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_rotate180_yuv420(src, dest, fmt->fmt.pix.width, + fmt->fmt.pix.height); + break; + } + } else if (hflip) { + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_hflip_rgbbgr24(src, dest, fmt); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_hflip_yuv420(src, dest, fmt); + break; + } + } else if (vflip) { + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_vflip_rgbbgr24(src, dest, fmt); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_vflip_yuv420(src, dest, fmt); + break; + } + } + + /* Our newly written data has no padding */ + v4lconvert_fixup_fmt(fmt); +} diff --git a/libv4lconvert/helper-funcs.h b/libv4lconvert/helper-funcs.h new file mode 100644 index 0000000..8ce1234 --- /dev/null +++ b/libv4lconvert/helper-funcs.h @@ -0,0 +1,79 @@ +/* Utility functions for decompression helpers + * + * Copyright (c) 2009 Hans de Goede + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +static int v4lconvert_helper_write(int fd, const void *b, size_t count, + char *progname) +{ + const unsigned char *buf = b; + size_t ret, written = 0; + + while (written < count) { + ret = write(fd, buf + written, count - written); + if (ret == -1) { + if (errno == EINTR) + continue; + + if (errno == EPIPE) /* Main program has quited */ + exit(0); + + fprintf(stderr, "%s: error writing: %s\n", progname, strerror(errno)); + return -1; + } + written += ret; + } + + return 0; +} + +static int v4lconvert_helper_read(int fd, void *b, size_t count, + char *progname) +{ + unsigned char *buf = b; + size_t ret, r = 0; + + while (r < count) { + ret = read(fd, buf + r, count - r); + if (ret == -1) { + if (errno == EINTR) + continue; + + fprintf(stderr, "%s: error reading: %s\n", progname, strerror(errno)); + return -1; + } + if (ret == 0) /* EOF */ + exit(0); + + r += ret; + } + + return 0; +} diff --git a/libv4lconvert/helper.c b/libv4lconvert/helper.c new file mode 100644 index 0000000..e5879fd --- /dev/null +++ b/libv4lconvert/helper.c @@ -0,0 +1,219 @@ +/* +# (C) 2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "libv4lconvert-priv.h" + +#define READ_END 0 +#define WRITE_END 1 + +/* Unfortunately I've failed in contact some Authors of decompression + code of out of tree drivers. So I've no permission to relicense their code + their code from GPL to LGPL. To work around this, these decompression + algorithms are put in separate executables and we pipe data through these + to decompress. + + The "protocol" is very simple: + + From libv4l to the helper the following is send: + int width + int height + int flags + int data length + unsigned char[] data (data length long) + + From the helper to libv4l the following is send: + int data length (-1 in case of a decompression error) + unsigned char[] data (not present when a decompression error happened) + */ + +static int v4lconvert_helper_start(struct v4lconvert_data *data, + const char *helper) +{ + if (pipe(data->decompress_in_pipe)) { + V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno)); + goto error; + } + + if (pipe(data->decompress_out_pipe)) { + V4LCONVERT_ERR("with helper pipe: %s\n", strerror(errno)); + goto error_close_in_pipe; + } + + data->decompress_pid = fork(); + if (data->decompress_pid == -1) { + V4LCONVERT_ERR("with helper fork: %s\n", strerror(errno)); + goto error_close_out_pipe; + } + + if (data->decompress_pid == 0) { + /* We are the child */ + + /* Closed unused read / write end of the pipes */ + close(data->decompress_out_pipe[WRITE_END]); + close(data->decompress_in_pipe[READ_END]); + + /* Connect stdin / out to the pipes */ + if (dup2(data->decompress_out_pipe[READ_END], STDIN_FILENO) == -1) { + perror("libv4lconvert: error with helper dup2"); + exit(1); + } + if (dup2(data->decompress_in_pipe[WRITE_END], STDOUT_FILENO) == -1) { + perror("libv4lconvert: error with helper dup2"); + exit(1); + } + + /* And execute the helper */ + execl(helper, helper, NULL); + + /* We should never get here */ + perror("libv4lconvert: error starting helper"); + exit(1); + } else { + /* Closed unused read / write end of the pipes */ + close(data->decompress_out_pipe[READ_END]); + close(data->decompress_in_pipe[WRITE_END]); + } + + return 0; + +error_close_out_pipe: + close(data->decompress_out_pipe[READ_END]); + close(data->decompress_out_pipe[WRITE_END]); +error_close_in_pipe: + close(data->decompress_in_pipe[READ_END]); + close(data->decompress_in_pipe[WRITE_END]); +error: + return -1; +} + +/* IMPROVE ME: we could block SIGPIPE here using pthread_sigmask() + and then in case of EPIPE consume the signal using + sigtimedwait (we need to check if a blocked signal wasn't present + before the write, otherwise we will consume that) and + after consuming the signal try to restart the helper. + + Note we currently do not do this, as SIGPIPE only happens if the + decompressor crashes, which in case of an embedded decompressor + would mean end of program, so by not handling SIGPIPE we treat + external decompressors identical. */ +static int v4lconvert_helper_write(struct v4lconvert_data *data, + const void *b, size_t count) +{ + const unsigned char *buf = b; + size_t ret, written = 0; + + while (written < count) { + ret = write(data->decompress_out_pipe[WRITE_END], buf + written, + count - written); + if ((int)ret == -1) { + if (errno == EINTR) + continue; + + V4LCONVERT_ERR("writing to helper: %s\n", strerror(errno)); + return -1; + } + written += ret; + } + + return 0; +} + +static int v4lconvert_helper_read(struct v4lconvert_data *data, void *b, + size_t count) +{ + unsigned char *buf = b; + size_t ret, r = 0; + + while (r < count) { + ret = read(data->decompress_in_pipe[READ_END], buf + r, count - r); + if ((int)ret == -1) { + if (errno == EINTR) + continue; + + V4LCONVERT_ERR("reading from helper: %s\n", strerror(errno)); + return -1; + } + if (ret == 0) { + V4LCONVERT_ERR("reading from helper: unexpected EOF\n"); + return -1; + } + r += ret; + } + + return 0; +} + +int v4lconvert_helper_decompress(struct v4lconvert_data *data, + const char *helper, const unsigned char *src, int src_size, + unsigned char *dest, int dest_size, int width, int height, int flags) +{ + int r; + + if (data->decompress_pid == -1) { + if (v4lconvert_helper_start(data, helper)) + return -1; + } + + if (v4lconvert_helper_write(data, &width, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &height, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &flags, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, &src_size, sizeof(int))) + return -1; + + if (v4lconvert_helper_write(data, src, src_size)) + return -1; + + if (v4lconvert_helper_read(data, &r, sizeof(int))) + return -1; + + if (r < 0) { + V4LCONVERT_ERR("decompressing frame data\n"); + return -1; + } + + if (dest_size < r) { + V4LCONVERT_ERR("destination buffer to small\n"); + return -1; + } + + return v4lconvert_helper_read(data, dest, r); +} + +void v4lconvert_helper_cleanup(struct v4lconvert_data *data) +{ + int status; + + if (data->decompress_pid != -1) { + close(data->decompress_out_pipe[WRITE_END]); + close(data->decompress_in_pipe[READ_END]); + waitpid(data->decompress_pid, &status, 0); + data->decompress_pid = -1; + } +} diff --git a/libv4lconvert/hm12.c b/libv4lconvert/hm12.c new file mode 100644 index 0000000..7631719 --- /dev/null +++ b/libv4lconvert/hm12.c @@ -0,0 +1,158 @@ +/* + +cx2341x HM12 conversion routines + +(C) 2009 Hans Verkuil + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License, version 2.1, +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + + */ + +#include "libv4lconvert-priv.h" +#include + +/* The HM12 format is used in the Conexant cx23415/6/8 MPEG encoder devices. + It is a macroblock format with separate Y and UV planes, each plane + consisting of 16x16 values. All lines are always 720 bytes long. If the + width of the image is less than 720, then the remainder is padding. + + The height has to be a multiple of 32 in order to get correct chroma + values. + + It is basically a by-product of the MPEG encoding inside the device, + which is available for raw video as a 'bonus feature'. + */ + +#define CLIP(color) \ + (unsigned char)(((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color))) + +static const int stride = 720; + +static void v4lconvert_hm12_to_rgb(const unsigned char *src, unsigned char *dest, + int width, int height, int rgb) +{ + unsigned int y, x, i, j; + const unsigned char *y_base = src; + const unsigned char *uv_base = src + stride * height; + const unsigned char *src_y; + const unsigned char *src_uv; + int mb_size = 256; + int r = rgb ? 0 : 2; + int b = 2 - r; + + for (y = 0; y < (unsigned)height; y += 16) { + int mb_y = (y / 16) * (stride / 16); + int mb_uv = (y / 32) * (stride / 16); + int maxy = (height - y < 16 ? height - y : 16); + + for (x = 0; x < (unsigned)width; x += 16, mb_y++, mb_uv++) { + int maxx = (width - x < 16 ? width - x : 16); + + src_y = y_base + mb_y * mb_size; + src_uv = uv_base + mb_uv * mb_size; + + if (y & 0x10) + src_uv += mb_size / 2; + + for (i = 0; i < (unsigned)maxy; i++) { + int idx = (x + (y + i) * width) * 3; + + for (j = 0; j < (unsigned)maxx; j++) { + int y = src_y[j]; + int u = src_uv[j & ~1]; + int v = src_uv[j | 1]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + dest[idx+r] = CLIP(y + v1); + dest[idx+1] = CLIP(y - rg); + dest[idx+b] = CLIP(y + u1); + idx += 3; + } + src_y += 16; + if (i & 1) + src_uv += 16; + } + } + } +} + +void v4lconvert_hm12_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + v4lconvert_hm12_to_rgb(src, dest, width, height, 1); +} + +void v4lconvert_hm12_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + v4lconvert_hm12_to_rgb(src, dest, width, height, 0); +} + +static void de_macro_uv(unsigned char *dstu, unsigned char *dstv, + const unsigned char *src, int w, int h) +{ + unsigned int y, x, i, j; + + for (y = 0; y < (unsigned)h; y += 16) { + for (x = 0; x < (unsigned)w; x += 8) { + const unsigned char *src_uv = src + y * stride + x * 32; + int maxy = (h - y < 16 ? h - y : 16); + int maxx = (w - x < 8 ? w - x : 8); + + for (i = 0; i < (unsigned)maxy; i++) { + int idx = x + (y + i) * w; + + for (j = 0; j < (unsigned)maxx; j++) { + dstu[idx+j] = src_uv[2 * j]; + dstv[idx+j] = src_uv[2 * j + 1]; + } + src_uv += 16; + } + } + } +} + +static void de_macro_y(unsigned char *dst, const unsigned char *src, + int w, int h) +{ + unsigned int y, x, i; + + for (y = 0; y < (unsigned)h; y += 16) { + for (x = 0; x < (unsigned)w; x += 16) { + const unsigned char *src_y = src + y * stride + x * 16; + int maxy = (h - y < 16 ? h - y : 16); + int maxx = (w - x < 16 ? w - x : 16); + + for (i = 0; i < (unsigned)maxy; i++) { + memcpy(dst + x + (y + i) * w, src_y, maxx); + src_y += 16; + } + } + } +} + +void v4lconvert_hm12_to_yuv420(const unsigned char *src, unsigned char *dest, + int width, int height, int yvu) +{ + de_macro_y(dest, src, width, height); + dest += width * height; + src += stride * height; + if (yvu) + de_macro_uv(dest + width * height / 4, dest, src, width / 2, height / 2); + else + de_macro_uv(dest, dest + width * height / 4, src, width / 2, height / 2); +} diff --git a/libv4lconvert/jidctflt.c b/libv4lconvert/jidctflt.c new file mode 100644 index 0000000..7e2a87f --- /dev/null +++ b/libv4lconvert/jidctflt.c @@ -0,0 +1,284 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * + * The authors make NO WARRANTY or representation, either express or implied, + * with respect to this software, its quality, accuracy, merchantability, or + * fitness for a particular purpose. This software is provided "AS IS", and you, + * its user, assume the entire risk as to its quality and accuracy. + * + * This software is copyright (C) 1991-1998, Thomas G. Lane. + * All Rights Reserved except as specified below. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * software (or portions thereof) for any purpose, without fee, subject to these + * conditions: + * (1) If any part of the source code for this software is distributed, then this + * README file must be included, with this copyright and no-warranty notice + * unaltered; and any additions, deletions, or changes to the original files + * must be clearly indicated in accompanying documentation. + * (2) If only executable code is distributed, then the accompanying + * documentation must state that "this software is based in part on the work of + * the Independent JPEG Group". + * (3) Permission for use of this software is granted only if the user accepts + * full responsibility for any undesirable consequences; the authors accept + * NO LIABILITY for damages of any kind. + * + * These conditions apply to any software derived from or based on the IJG code, + * not just to the unmodified library. If you use our work, you ought to + * acknowledge us. + * + * Permission is NOT granted for the use of any IJG author's name or company name + * in advertising or publicity relating to this software or products derived from + * it. This software may be referred to only as "the Independent JPEG Group's + * software". + * + * We specifically permit and encourage the use of this software as the basis of + * commercial products, provided that all warranty or liability claims are + * assumed by the product vendor. + * + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. +*/ + +#include +#include "tinyjpeg-internal.h" + +#define FAST_FLOAT float +#define DCTSIZE 8 +#define DCTSIZE2 (DCTSIZE * DCTSIZE) + +#define DEQUANTIZE(coef, quantval) (((FAST_FLOAT) (coef)) * (quantval)) + +#if defined(__GNUC__) && (defined(__i686__) || defined(__x86_64__)) + +static inline unsigned char descale_and_clamp(int x, int shift) +{ + __asm__ ( + "add %3,%1\n" + "\tsar %2,%1\n" + "\tsub $-128,%1\n" + "\tcmovl %5,%1\n" /* Use the sub to compare to 0 */ + "\tcmpl %4,%1\n" + "\tcmovg %4,%1\n" + : "=r"(x) + : "0"(x), "Ic"((unsigned char)shift), "ir" (1U << (shift - 1)), "r" (0xff), "r" (0) + ); + return x; +} + +#else +static inline unsigned char descale_and_clamp(int x, int shift) +{ + x += 1UL << (shift - 1); + if (x < 0) + x = (x >> shift) | ((~(0UL)) << (32 - (shift))); + else + x >>= shift; + x += 128; + if (x > 255) + return 255; + if (x < 0) + return 0; + return x; +} +#endif + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +void tinyjpeg_idct_float(struct component *compptr, uint8_t *output_buf, int stride) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + int16_t *inptr; + FAST_FLOAT *quantptr; + FAST_FLOAT *wsptr; + uint8_t *outptr; + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = compptr->DCT; + quantptr = compptr->Q_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + outptr = output_buf; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = descale_and_clamp((int)(tmp0 + tmp7), 3); + outptr[7] = descale_and_clamp((int)(tmp0 - tmp7), 3); + outptr[1] = descale_and_clamp((int)(tmp1 + tmp6), 3); + outptr[6] = descale_and_clamp((int)(tmp1 - tmp6), 3); + outptr[2] = descale_and_clamp((int)(tmp2 + tmp5), 3); + outptr[5] = descale_and_clamp((int)(tmp2 - tmp5), 3); + outptr[4] = descale_and_clamp((int)(tmp3 + tmp4), 3); + outptr[3] = descale_and_clamp((int)(tmp3 - tmp4), 3); + + + wsptr += DCTSIZE; /* advance pointer to next row */ + outptr += stride; + } +} + diff --git a/libv4lconvert/jl2005bcd.c b/libv4lconvert/jl2005bcd.c new file mode 100644 index 0000000..05af896 --- /dev/null +++ b/libv4lconvert/jl2005bcd.c @@ -0,0 +1,219 @@ +/* jl2005bcd.c + * + * Converts raw output from Jeilin JL2005B/C/D to Bayer output. + * + * The code is based upon what is found in libgphoto2/camlibs/jl2005c, + * Copyright (c) 2010 Theodore Kilgore + * + * The decompression code used is + * Copyright (c) 2010 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include + +#ifdef HAVE_JPEG +#include "libv4lconvert-priv.h" +#include "jpeg_memsrcdest.h" +#include "libv4lsyscall-priv.h" + + +#define JPEG_HEADER_SIZE 338 +#define JPEG_HEIGHT_OFFSET 94 + +static int find_eoi(struct v4lconvert_data *data, + const unsigned char *jpeg_data, int jpeg_data_idx, int jpeg_data_size) +{ + int i; + + for (i = jpeg_data_idx; i < (jpeg_data_size - 1); i++) + if (jpeg_data[i] == 0xff && jpeg_data[i + 1] == 0xd9) + break; + + if (i >= (jpeg_data_size - 1)) { + V4LCONVERT_ERR("incomplete jl2005bcd frame\n"); + return -1; + } + + return i + 2; /* + 2 -> Point to after EOI marker */ +} + + +int v4lconvert_decode_jl2005bcd(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height) +{ + unsigned char jpeg_stripe[50000]; + int q; + struct jpeg_compress_struct cinfo; + struct jpeg_decompress_struct dinfo; + struct jpeg_error_mgr jcerr, jderr; + JOCTET *jpeg_header = NULL; + unsigned long jpeg_header_size = 0; + int i, x, y, x1, y1, jpeg_data_size, jpeg_data_idx, eoi, size; + + /* src_size had better be bigger than 16 */ + if (src_size < 16) + return 1; + + /* The first 16 bytes of src are a raw frame header */ + q = src[13] & 0x7f; + + if (height != src[4] << 3) { + V4LCONVERT_ERR("Height is %d, not %d\n", src[4] << 3, height); + return 1; + } + + if (width != src[5] << 3) { + V4LCONVERT_ERR("Width is %d, not %d\n", src[5] << 3, width); + return 1; + } + + /* + * And the fun begins, first of all create a dummy jpeg, which we use + * to get the headers from to feed to libjpeg when decompressing the + * stripes. This way we can use libjpeg's quant table handling + * (and built in default huffman tables). + */ + cinfo.err = jpeg_std_error (&jcerr); + jpeg_create_compress (&cinfo); + jpeg_mem_dest (&cinfo, &jpeg_header, &jpeg_header_size); + cinfo.image_width = 16; + cinfo.image_height = 16; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults (&cinfo); + /* Make comp[0] (which will be green) 1x2 subsampled */ + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 2; + /* Make comp[1] and [2] use huffman table and quanttable 0, as all + * components use luminance settings with the jl2005c/d/e */ + cinfo.comp_info[1].quant_tbl_no = 0; + cinfo.comp_info[1].dc_tbl_no = 0; + cinfo.comp_info[1].ac_tbl_no = 0; + cinfo.comp_info[2].quant_tbl_no = 0; + cinfo.comp_info[2].dc_tbl_no = 0; + cinfo.comp_info[2].ac_tbl_no = 0; + /* Apply the quality setting from the header */ + if (q <= 0) + i = 5000; + else if (q <= 50) + i = 5000 / q; + else if (q <= 100) + i = 2 * (100 - q); + else + i = 0; + jpeg_set_linear_quality(&cinfo, i, TRUE); + + jpeg_start_compress (&cinfo, TRUE); + while( cinfo.next_scanline < cinfo.image_height ) { + JOCTET row[16 * 3]; + JSAMPROW row_pointer[1] = { row }; + jpeg_write_scanlines (&cinfo, row_pointer, 1); + } + jpeg_finish_compress (&cinfo); + jpeg_destroy_compress (&cinfo); + + JSAMPLE green[8 * 16]; + JSAMPLE red[8 * 8]; + JSAMPLE blue[8 * 8]; + JSAMPROW green_row_pointer[16]; + JSAMPROW red_row_pointer[8]; + JSAMPROW blue_row_pointer[8]; + + for (i = 0; i < 16; i++) + green_row_pointer[i] = green + i * 8; + + for (i = 0; i < 8; i++) { + red_row_pointer[i] = red + i * 8; + blue_row_pointer[i] = blue + i * 8; + } + + JSAMPARRAY samp_image[3] = { green_row_pointer, + red_row_pointer, + blue_row_pointer }; + + memcpy(jpeg_stripe, jpeg_header, JPEG_HEADER_SIZE); + jpeg_stripe[JPEG_HEIGHT_OFFSET ] = height >> 8; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 1] = height; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 2] = 0; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 3] = 8; + free (jpeg_header); + /* Get past the raw frame header. */ + src += 16; + jpeg_data_size = src_size - 16; + + jpeg_data_idx = 0; + + + dinfo.err = jpeg_std_error (&jderr); + jpeg_create_decompress (&dinfo); + for (x = 0; x < width; x += 16) { + eoi = find_eoi(data, src, jpeg_data_idx, jpeg_data_size); + if (eoi < 0) + return eoi; + + size = eoi - jpeg_data_idx; + if ((JPEG_HEADER_SIZE + size) > sizeof(jpeg_stripe)) { + V4LCONVERT_ERR("stripe size too big %d > %zd\n", + JPEG_HEADER_SIZE + size, + sizeof(jpeg_stripe)); + return 1; + } + memcpy (jpeg_stripe + JPEG_HEADER_SIZE, + src + jpeg_data_idx, size); + + jpeg_mem_src (&dinfo, jpeg_stripe, JPEG_HEADER_SIZE + size); + jpeg_read_header (&dinfo, TRUE); + dinfo.raw_data_out = TRUE; +#if JPEG_LIB_VERSION >= 70 + dinfo.do_fancy_upsampling = FALSE; +#endif + jpeg_start_decompress (&dinfo); + for (y = 0; y < height; y += 16) { + jpeg_read_raw_data (&dinfo, samp_image, 16); + for (y1 = 0; y1 < 16; y1 += 2) { + for (x1 = 0; x1 < 16; x1 += 2) { + dest[((y + y1 + 0) * width + + x + x1 + 0)] + = red[y1 * 4 + x1 / 2]; + dest[((y + y1 + 0) * width + + x + x1 + 1)] + = green[y1 * 8 + x1 / 2]; + dest[((y + y1 + 1) * width + + x + x1 + 0)] + = green[y1 * 8 + 8 + x1 / 2]; + dest[((y + y1 + 1) * width + + x + x1 + 1)] + = blue[y1 * 4 + x1 / 2]; + } + } + } + jpeg_finish_decompress (&dinfo); + + /* Set jpeg_data_idx for the next stripe */ + jpeg_data_idx = (jpeg_data_idx + size + 0x0f) & ~0x0f; + } + jpeg_destroy_decompress(&dinfo); + return 0; +} + +#endif /* HAVE_JPEG */ diff --git a/libv4lconvert/jpeg.c b/libv4lconvert/jpeg.c new file mode 100644 index 0000000..ba6ac96 --- /dev/null +++ b/libv4lconvert/jpeg.c @@ -0,0 +1,428 @@ +/* +# (C) 2008-2011 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include "libv4lconvert-priv.h" +#ifdef HAVE_JPEG +#include "jpeg_memsrcdest.h" +#endif + +int v4lconvert_decode_jpeg_tinyjpeg(struct v4lconvert_data *data, + unsigned char *src, int src_size, unsigned char *dest, + struct v4l2_format *fmt, unsigned int dest_pix_fmt, int flags) +{ + int result = 0; + unsigned char *components[3]; + unsigned int header_width, header_height; + unsigned int width = fmt->fmt.pix.width; + unsigned int height = fmt->fmt.pix.height; + + if (!data->tinyjpeg) { + data->tinyjpeg = tinyjpeg_init(); + if (!data->tinyjpeg) + return v4lconvert_oom_error(data); + } + flags |= TINYJPEG_FLAGS_MJPEG_TABLE; + tinyjpeg_set_flags(data->tinyjpeg, flags); + if (tinyjpeg_parse_header(data->tinyjpeg, src, src_size)) { + V4LCONVERT_ERR("parsing JPEG header: %s", + tinyjpeg_get_errorstring(data->tinyjpeg)); + errno = EAGAIN; + return -1; + } + tinyjpeg_get_size(data->tinyjpeg, &header_width, &header_height); + + if (data->control_flags & V4LCONTROL_ROTATED_90_JPEG) { + unsigned int tmp = width; + width = height; + height = tmp; + } + + if (header_width != width || header_height != height) { + V4LCONVERT_ERR("unexpected width / height in JPEG header: " + "expected: %ux%u, header: %ux%u\n", + width, height, header_width, header_height); + errno = EIO; + return -1; + } + fmt->fmt.pix.width = header_width; + fmt->fmt.pix.height = header_height; + + components[0] = dest; + + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + tinyjpeg_set_components(data->tinyjpeg, components, 1); + result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_RGB24); + break; + case V4L2_PIX_FMT_BGR24: + tinyjpeg_set_components(data->tinyjpeg, components, 1); + result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_BGR24); + break; + case V4L2_PIX_FMT_YUV420: + components[1] = components[0] + width * height; + components[2] = components[1] + width * height / 4; + tinyjpeg_set_components(data->tinyjpeg, components, 3); + result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_YUV420P); + break; + case V4L2_PIX_FMT_YVU420: + components[2] = components[0] + width * height; + components[1] = components[2] + width * height / 4; + tinyjpeg_set_components(data->tinyjpeg, components, 3); + result = tinyjpeg_decode(data->tinyjpeg, TINYJPEG_FMT_YUV420P); + break; + } + + if (result) { + /* The JPEG header checked out ok but we got an error + during decompression. Some webcams, esp pixart and + sn9c20x based ones regulary generate corrupt frames, + which are best thrown away to avoid flashes in the + video stream. We use EPIPE to signal the upper layer + we have some video data, but it is incomplete. + + The upper layer (usually libv4l2) should respond to + this by trying a number of times to get a new frame + and if that fails just passing up whatever we did + manage to decompress. */ + V4LCONVERT_ERR("decompressing JPEG: %s", + tinyjpeg_get_errorstring(data->tinyjpeg)); + errno = EPIPE; + return -1; + } + return 0; +} + +#ifdef HAVE_JPEG + +static void jerr_error_exit(j_common_ptr cinfo) +{ + struct v4lconvert_data *data = cinfo->client_data; + + longjmp(data->jerr_jmp_state, data->jerr_errno); +} + +static void jerr_emit_message(j_common_ptr cinfo, int msg_level) +{ + char buffer[JMSG_LENGTH_MAX]; + struct v4lconvert_data *data = cinfo->client_data; + + /* < -1 error, == -1 warning, >= 0 trace */ + if (msg_level < -1) + return; + + cinfo->err->format_message(cinfo, buffer); + snprintf(data->error_msg, V4LCONVERT_ERROR_MSG_SIZE, + "v4l-convert: libjpeg error: %s\n", buffer); +} + +static void init_libjpeg_cinfo(struct v4lconvert_data *data) +{ + struct jpeg_compress_struct cinfo; + unsigned char *jpeg_header = NULL; + unsigned long jpeg_header_size = 0; + + if (data->cinfo_initialized) + return; + + /* Setup our error handling */ + jpeg_std_error(&data->jerr); + data->jerr.error_exit = jerr_error_exit; + data->jerr.emit_message = jerr_emit_message; + + /* Create a jpeg compression object with default params and write + default jpeg headers to a mem buffer, so that we can use them to + pre-fill a jpeg_decompress_struct with default quant and huffman + tables, so that libjpeg can be used to parse [m]jpg-s with + incomplete headers */ + cinfo.err = &data->jerr; + cinfo.client_data = data; + jpeg_create_compress(&cinfo); + jpeg_mem_dest(&cinfo, &jpeg_header, &jpeg_header_size); + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + jpeg_write_tables(&cinfo); + jpeg_destroy_compress(&cinfo); + + /* Init the jpeg_decompress_struct */ + data->cinfo.err = &data->jerr; + data->cinfo.client_data = data; + jpeg_create_decompress(&data->cinfo); + jpeg_mem_src(&data->cinfo, jpeg_header, jpeg_header_size); + jpeg_read_header(&data->cinfo, FALSE); + + free(jpeg_header); + data->cinfo_initialized = 1; +} + +static int decode_libjpeg_h_samp1(struct v4lconvert_data *data, + unsigned char *ydest, unsigned char *udest, unsigned char *vdest, + int v_samp) +{ + struct jpeg_decompress_struct *cinfo = &data->cinfo; + int x, y; + unsigned char *uv_buf; + unsigned int width = cinfo->image_width; + JSAMPROW y_rows[16], u_rows[8], v_rows[8]; + JSAMPARRAY rows[3] = { y_rows, u_rows, v_rows }; + + uv_buf = v4lconvert_alloc_buffer(width * 16, + &data->convert_pixfmt_buf, + &data->convert_pixfmt_buf_size); + if (!uv_buf) + return v4lconvert_oom_error(data); + + for (y = 0; y < 8; y++) { + u_rows[y] = uv_buf; + uv_buf += width; + v_rows[y] = uv_buf; + uv_buf += width; + } + uv_buf -= width * 16; + + while (cinfo->output_scanline < cinfo->image_height) { + for (y = 0; y < 8 * v_samp; y++) { + y_rows[y] = ydest; + ydest += cinfo->image_width; + } + y = jpeg_read_raw_data(cinfo, rows, 8 * v_samp); + if (y != 8 * v_samp) + return -1; + + /* For v_samp == 1 skip copying uv vals every other time */ + if (cinfo->output_scanline % 16) + continue; + + /* Copy over every other u + v pixel for 8 lines */ + for (y = 0; y < 8; y++) { + for (x = 0; x < width; x += 2) { + *udest++ = *uv_buf++; + uv_buf++; + } + for (x = 0; x < width; x += 2) { + *vdest++ = *uv_buf++; + uv_buf++; + } + } + uv_buf -= width * 16; + } + return 0; +} + +static int decode_libjpeg_h_samp2(struct v4lconvert_data *data, + unsigned char *ydest, unsigned char *udest, unsigned char *vdest, + int v_samp) +{ + struct jpeg_decompress_struct *cinfo = &data->cinfo; + int y; + unsigned int width = cinfo->image_width; + JSAMPROW y_rows[16], u_rows[8], v_rows[8]; + JSAMPARRAY rows[3] = { y_rows, u_rows, v_rows }; + + while (cinfo->output_scanline < cinfo->image_height) { + for (y = 0; y < 8 * v_samp; y++) { + y_rows[y] = ydest; + ydest += width; + } + /* + * For v_samp == 1 were going to get 1 set of uv values per + * line, but we need only 1 set per 2 lines since our output + * has v_samp == 2. We store every 2 sets in 1 line, + * effectively using the second set for each output line. + */ + if (v_samp == 1) { + for (y = 0; y < 8; y++) { + u_rows[y] = udest; + v_rows[y] = vdest; + y++; + u_rows[y] = udest; + v_rows[y] = vdest; + udest += width / 2; + vdest += width / 2; + } + } else { /* v_samp == 2 */ + for (y = 0; y < 8; y++) { + u_rows[y] = udest; + v_rows[y] = vdest; + udest += width / 2; + vdest += width / 2; + } + } + + y = jpeg_read_raw_data(cinfo, rows, 8 * v_samp); + if (y != 8 * v_samp) + return -1; + } + return 0; +} + +int v4lconvert_decode_jpeg_libjpeg(struct v4lconvert_data *data, + unsigned char *src, int src_size, unsigned char *dest, + struct v4l2_format *fmt, unsigned int dest_pix_fmt) +{ + unsigned int width = fmt->fmt.pix.width; + unsigned int height = fmt->fmt.pix.height; + int result = 0; + + /* libjpeg errors before decoding the first line should signal EAGAIN */ + data->jerr_errno = EAGAIN; + result = setjmp(data->jerr_jmp_state); + if (result) { + if (data->cinfo_initialized) + jpeg_abort_decompress(&data->cinfo); + errno = result; + return -1; + } + + init_libjpeg_cinfo(data); + + jpeg_mem_src(&data->cinfo, src, src_size); + jpeg_read_header(&data->cinfo, TRUE); + + if (data->cinfo.image_width != width || + data->cinfo.image_height != height) { + V4LCONVERT_ERR("unexpected width / height in JPEG header: " + "expected: %ux%u, header: %ux%u\n", width, + height, data->cinfo.image_width, + data->cinfo.image_height); + errno = EIO; + return -1; + } + + if (data->cinfo.num_components != 3) { + V4LCONVERT_ERR("unexpected no components in JPEG: %d\n", + data->cinfo.num_components); + errno = EIO; + return -1; + } + + if (dest_pix_fmt == V4L2_PIX_FMT_RGB24 || + dest_pix_fmt == V4L2_PIX_FMT_BGR24) { + JSAMPROW row_pointer[1]; + +#ifdef JCS_EXTENSIONS + if (dest_pix_fmt == V4L2_PIX_FMT_BGR24) + data->cinfo.out_color_space = JCS_EXT_BGR; +#endif + row_pointer[0] = dest; + jpeg_start_decompress(&data->cinfo); + /* Make libjpeg errors report that we've got some data */ + data->jerr_errno = EPIPE; + while (data->cinfo.output_scanline < height) { + jpeg_read_scanlines(&data->cinfo, row_pointer, 1); + row_pointer[0] += 3 * width; + } + jpeg_finish_decompress(&data->cinfo); +#ifndef JCS_EXTENSIONS + if (dest_pix_fmt == V4L2_PIX_FMT_BGR24) + v4lconvert_swap_rgb(dest, dest, width, height); +#endif + } else { + int h_samp, v_samp; + unsigned char *udest, *vdest; + + if (data->cinfo.max_h_samp_factor == 2 && + data->cinfo.cur_comp_info[0]->h_samp_factor == 2 && + data->cinfo.cur_comp_info[1]->h_samp_factor == 1 && + data->cinfo.cur_comp_info[2]->h_samp_factor == 1) { + h_samp = 2; +#if 0 /* HDG: untested, disable for now */ + } else if (data->cinfo.max_h_samp_factor == 1 && + data->cinfo.cur_comp_info[0]->h_samp_factor == 1 && + data->cinfo.cur_comp_info[1]->h_samp_factor == 1 && + data->cinfo.cur_comp_info[2]->h_samp_factor == 1) { + h_samp = 1; +#endif + } else { + fprintf(stderr, + "libv4lconvert: unsupported jpeg h-sampling " + "factors %d:%d:%d, please report this to " + "hdegoede@redhat.com\n", + data->cinfo.cur_comp_info[0]->h_samp_factor, + data->cinfo.cur_comp_info[1]->h_samp_factor, + data->cinfo.cur_comp_info[2]->h_samp_factor); + errno = EOPNOTSUPP; + return -1; + } + + if (data->cinfo.max_v_samp_factor == 2 && + data->cinfo.cur_comp_info[0]->v_samp_factor == 2 && + data->cinfo.cur_comp_info[1]->v_samp_factor == 1 && + data->cinfo.cur_comp_info[2]->v_samp_factor == 1) { + v_samp = 2; + } else if (data->cinfo.max_v_samp_factor == 1 && + data->cinfo.cur_comp_info[0]->v_samp_factor == 1 && + data->cinfo.cur_comp_info[1]->v_samp_factor == 1 && + data->cinfo.cur_comp_info[2]->v_samp_factor == 1) { + v_samp = 1; + } else { + fprintf(stderr, + "libv4lconvert: unsupported jpeg v-sampling " + "factors %d:%d:%d, please report this to " + "hdegoede@redhat.com\n", + data->cinfo.cur_comp_info[0]->v_samp_factor, + data->cinfo.cur_comp_info[1]->v_samp_factor, + data->cinfo.cur_comp_info[2]->v_samp_factor); + errno = EOPNOTSUPP; + return -1; + } + + /* We don't want any padding as that may overflow our dest */ + if (width % (8 * h_samp) || height % (8 * v_samp)) { + V4LCONVERT_ERR( + "resolution is not a multiple of dctsize"); + errno = EIO; + return -1; + } + + if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) { + vdest = dest + width * height; + udest = vdest + (width * height) / 4; + } else { + udest = dest + width * height; + vdest = udest + (width * height) / 4; + } + + data->cinfo.raw_data_out = TRUE; + data->cinfo.do_fancy_upsampling = FALSE; + jpeg_start_decompress(&data->cinfo); + /* Make libjpeg errors report that we've got some data */ + data->jerr_errno = EPIPE; + if (h_samp == 1) { + result = decode_libjpeg_h_samp1(data, dest, udest, + vdest, v_samp); + } else { + result = decode_libjpeg_h_samp2(data, dest, udest, + vdest, v_samp); + } + if (result) + jpeg_abort_decompress(&data->cinfo); + else + jpeg_finish_decompress(&data->cinfo); + } + + return result; +} + +#endif // HAVE_JPEG diff --git a/libv4lconvert/jpeg_memsrcdest.c b/libv4lconvert/jpeg_memsrcdest.c new file mode 100644 index 0000000..323e7af --- /dev/null +++ b/libv4lconvert/jpeg_memsrcdest.c @@ -0,0 +1,313 @@ +/* +* memsrc.c +* +* Copyright (C) 1994-1996, Thomas G. Lane. +* This file is part of the Independent JPEG Group's software. +* For conditions of distribution and use, see the accompanying README file. +* +* This file contains decompression data source routines for the case of +* reading JPEG data from a memory buffer that is preloaded with the entire +* JPEG file. This would not seem especially useful at first sight, but +* a number of people have asked for it. +* This is really just a stripped-down version of jdatasrc.c. Comparison +* of this code with jdatasrc.c may be helpful in seeing how to make +* custom source managers for other purposes. +*/ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include + +#ifdef HAVE_JPEG +#include +#include +#include "jpeg_memsrcdest.h" + +/* libjpeg8 and later come with their own (API compatible) memory source + and dest */ +#if JPEG_LIB_VERSION < 80 + +/* Expanded data source object for memory input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + JOCTET eoi_buffer[2]; /* a place to put a dummy EOI */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + + +/* +* Initialize source --- called by jpeg_read_header +* before any data is actually read. +*/ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + /* No work, since jpeg_mem_src set up the buffer pointer and count. + * Indeed, if we want to read multiple JPEG images from one buffer, + * this *must* not do anything to the pointer. + */ +} + + +/* +* Fill the input buffer --- called whenever buffer is emptied. +* +* In this application, this routine should never be called; if it is called, +* the decompressor has overrun the end of the input buffer, implying we +* supplied an incomplete or corrupt JPEG datastream. A simple error exit +* might be the most appropriate response. +* +* But what we choose to do in this code is to supply dummy EOI markers +* in order to force the decompressor to finish processing and supply +* some sort of output image, no matter how corrupted. +*/ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + WARNMS(cinfo, JWRN_JPEG_EOF); + + /* Create a fake EOI marker */ + src->eoi_buffer[0] = (JOCTET) 0xFF; + src->eoi_buffer[1] = (JOCTET) JPEG_EOI; + src->pub.next_input_byte = src->eoi_buffer; + src->pub.bytes_in_buffer = 2; + + return TRUE; +} + + +/* +* Skip data --- used to skip over a potentially large amount of +* uninteresting data (such as an APPn marker). +* +* If we overrun the end of the buffer, we let fill_input_buffer deal with +* it. An extremely large skip could cause some time-wasting here, but +* it really isn't supposed to happen ... and the decompressor will never +* skip more than 64K anyway. +*/ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never + * return FALSE, so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* +* An additional method that can be provided by data source modules is the +* resync_to_restart method for error recovery in the presence of RST markers. +* For the moment, this source module just uses the default resync method +* provided by the JPEG library. That method assumes that no backtracking +* is possible. +*/ + + +/* +* Terminate source --- called by jpeg_finish_decompress +* after all data has been read. Often a no-op. +* +* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding +* application must deal with any cleanup that should happen even +* for error exit. +*/ + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* +* Prepare for input from a memory buffer. +*/ + +GLOBAL(void) +jpeg_mem_src (j_decompress_ptr cinfo, unsigned char * buffer, + unsigned long bufsize) +{ + my_src_ptr src; + + /* The source object is made permanent so that a series of JPEG images + * can be read from a single buffer by calling jpeg_mem_src + * only before the first one. + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, + JPOOL_PERMANENT, + sizeof(my_source_mgr)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + + src->pub.next_input_byte = buffer; + src->pub.bytes_in_buffer = bufsize; +} + + + +/* Memory destination source modelled after Thomas G. Lane's memory source + support and jdatadst.c + + Copyright (C) 2010, Hans de Goede + + This code may be used under the same conditions as Thomas G. Lane's memory + source (see the copyright header at the top of this file). + */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + JOCTET **buffer; /* start of buffer */ + unsigned long buf_size, *outsize; +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + +#define OUTPUT_BUF_SIZE 32768 /* choose an efficiently fwrite'able size */ + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + /* No work, since jpeg_mem_dest set up the buffer pointer and count. + * Indeed, if we want to write multiple JPEG images to one buffer, + * this *must* not do anything to the pointer. + */ +} + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + *dest->buffer = realloc (*dest->buffer, dest->buf_size + OUTPUT_BUF_SIZE); + if (!*dest->buffer) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + + dest->pub.next_output_byte = *dest->buffer + dest->buf_size; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + dest->buf_size += OUTPUT_BUF_SIZE; + + return TRUE; +} + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + *dest->outsize = dest->buf_size - dest->pub.free_in_buffer; +} + +GLOBAL(void) +jpeg_mem_dest (j_compress_ptr cinfo, unsigned char ** outbuffer, + unsigned long * outsize) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, + JPOOL_PERMANENT, + sizeof(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->buffer = outbuffer; + dest->buf_size = *outsize; + dest->outsize = outsize; + + if (*dest->buffer == NULL || dest->buf_size == 0) { + /* Allocate initial buffer */ + *dest->buffer = malloc(OUTPUT_BUF_SIZE); + if (*dest->buffer == NULL) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10); + dest->buf_size = OUTPUT_BUF_SIZE; + } + + dest->pub.next_output_byte = *dest->buffer; + dest->pub.free_in_buffer = dest->buf_size; +} + +#endif /* JPEG_LIB_VERSION < 80 */ +#endif /* HAVE_JPEG */ diff --git a/libv4lconvert/jpeg_memsrcdest.h b/libv4lconvert/jpeg_memsrcdest.h new file mode 100644 index 0000000..28a6477 --- /dev/null +++ b/libv4lconvert/jpeg_memsrcdest.h @@ -0,0 +1,13 @@ +#include + +#if JPEG_LIB_VERSION < 80 + +void +jpeg_mem_src (j_decompress_ptr cinfo, unsigned char * buffer, + unsigned long bufsize); + +void +jpeg_mem_dest (j_compress_ptr cinfo, unsigned char ** outbuffer, + unsigned long * outsize); + +#endif diff --git a/libv4lconvert/jpgl.c b/libv4lconvert/jpgl.c new file mode 100644 index 0000000..18bad29 --- /dev/null +++ b/libv4lconvert/jpgl.c @@ -0,0 +1,727 @@ +/* + * Implementation of JPEG Lite decoding algorithm + * + * Author & Copyright (c) 2003 : Sylvain Munaut + * + * v4l library adaptation: Jean-François Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + + * Note this code was originally licensed under the GNU GPL instead of the + * GNU LGPL, its license has been changed with permission, see the permission + * mail at the end of this file. + */ + +/* Original WebSite: nw802.sourceforge.net */ + +#include +#include "libv4lconvert-priv.h" + +#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) +#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) +#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri)]) + +struct RingQueue { + const unsigned char *queue; + int length; + int ri; +}; + +/* ============================================================================ + * RingQueue bit reader + * ============================================================================ + * All what is needed to read bit by nit from the RingQueue pump + * provided by usbvideo + * Critical part are macro and not functions to speed things up + * Rem: Data are read from the RingQueue as if they were 16bits Little Endian + * words. Most Significants Bits are outputed first. + */ + +/* Structure used to store what we need. */ +/* (We may need multiple simultaneous instance from several cam) */ +struct rqBitReader { + int cur_bit; + unsigned int cur_data; + struct RingQueue *rq; +}; + +static inline void rqBR_init( struct rqBitReader *br, struct RingQueue *rq ) +{ + br->cur_bit = 16; + br->cur_data = + RING_QUEUE_PEEK( rq, 2 ) | + RING_QUEUE_PEEK( rq, 3 ) << 8 | + RING_QUEUE_PEEK( rq, 0 ) << 16 | + RING_QUEUE_PEEK( rq, 1 ) << 24 ; + RING_QUEUE_DEQUEUE_BYTES( rq, 2 ); + br->rq = rq; +} + +#define rqBR_peekBits(br,n) ( br->cur_data >> (32-n) ) + +#define rqBR_flushBits(br,n) do { \ + br->cur_data <<= n; \ + if ( (br->cur_bit -= n) <= 0 ) { \ + br->cur_data |= \ + RING_QUEUE_PEEK( br->rq, 2 ) << -br->cur_bit | \ + RING_QUEUE_PEEK( br->rq, 3 ) << (8 - br->cur_bit); \ + RING_QUEUE_DEQUEUE_BYTES( br->rq, 2 ); \ + br->cur_bit += 16; \ + } \ + } while (0) + +/* ============================================================================ + * Real JPEG Lite stuff + * ============================================================================ + * + * Precomputed tables + * Theses are computed at init time to make real-time operations faster. + * It takes some space ( about 9k ). But believe me it worth it ! + */ + +/* Variable Lenght Coding related tables, used for AC coefficient decoding + * TODO Check that 7 bits is enough ! */ +static signed char vlcTbl_len[1<<10]; /* Meaningful bit count */ +static signed char vlcTbl_run[1<<10]; /* Run */ +static signed char vlcTbl_amp[1<<10]; /* Amplitude (without the sign) */ + +/* YUV->RGB conversion table */ +static int yuvTbl_y[256]; +static int yuvTbl_u1[256]; +static int yuvTbl_u2[256]; +static int yuvTbl_v1[256]; +static int yuvTbl_v2[256]; + +/* Clamping table */ +#define SAFE_CLAMP +#ifdef SAFE_CLAMP +static inline unsigned char clamp(int x) { + if (x > 255) + return 255; + if (x < 0) + return 0; + return x; +} +#define clamp_adjust(x) clamp(x+128) +#else +#define clamp(x) clampTbl[(x)+512] +#define clamp_adjust(x) clampTbl[(x)+640] +static char clampTbl[1280]; +#endif + +/* Code to initialize those tables */ +static void vlcTbl_init(void) +{ + /* Bases tables used to compute the bigger one + * To understands theses, look at the VLC doc in the + * US patent document. */ + + static const int vlc_num = 28; + static const int vlc_len[] = + { 2, 2, 3, 3, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 8 ,8 ,8 ,9, 9, 9, 10, 10, 10, 10, 10, 10 }; + static const int vlc_run[] = + { 0, 0, 0, 1, 0, 2, 3, 1, 0, 4, 0, 5, 1, 0, -1, -2, + 2, 6, 0, 3, 1, 0, 1, 0, 7, 2, 0, 8 }; + static const int vlc_amp[] = + { 0, 1, 2, 1, 3, 1, 1, 2, 4, 1 ,5 ,1 ,3 ,6, -1, -2, + 2, 1, 7, 2, 4, 8, 5, 9, 1 ,3, 10, 1 }; + static const int vlc_cod[] = + { 0x000, 0x002, 0x003, 0x006, 0x00E, 0x008, 0x00B, 0x012, + 0x014, 0x03D, 0x03E, 0x078, 0x079, 0x07E, 0x026, 0x027, + 0x054, 0x057, 0x0FF, 0x0AA, 0x0AC, 0x1FC, 0x156, 0x157, + 0x15A, 0x15B, 0x3FA, 0x3FB }; + + /* Vars */ + int i,j; + + /* Main filling loop */ + for ( i=0 ; i<(1<<10) ; i++ ) { + + /* Find the matching one */ + for ( j=0 ; j> (10-vlc_len[j])) == vlc_cod[j] ) { + if ( vlc_run[j] >= 0 ) + if ( vlc_amp[j] != 0 ) + vlcTbl_len[i] = vlc_len[j] + 1; + else + vlcTbl_len[i] = vlc_len[j]; /* EOB */ + else + vlcTbl_len[i] = 16; + vlcTbl_run[i] = vlc_run[j]; + vlcTbl_amp[i] = vlc_amp[j]; + break; + } + } + } +} + +static void yuvTbl_init(void) +{ + /* These tables are just pre-multiplied and pre-offseted + * YUV by the book + * R = 1.164 * (Y-16) + 1.596 * (U-128) + * G = 1.164 * (Y-16) - 0.813 * (U-128) - 0.391 * (V-128) + * B = 1.164 * (Y-16) + 2.018 * (V-128) */ + + int i; + + /* We use fixed point << 16 */ + for ( i=0 ; i < 256 ; i++ ) { + yuvTbl_y[i] = 76284 * (i- 16); + yuvTbl_u1[i] = 104595 * (i-128); + yuvTbl_u2[i] = 53281 * (i-128); + yuvTbl_v1[i] = 25625 * (i-128); + yuvTbl_v2[i] = 132252 * (i-128); + } +} + +#ifndef SAFE_CLAMP +static void clampTbl_init(void) +{ + /* Instead of doing if(...) to test for overrange, we use + * a clamping table */ + + int i; + + for (i=0 ; i < 512 ; i++) + clampTbl[i] = 0; + for (i=512 ; i < 768 ; i++ ) + clampTbl[i] = i - 512; + for (i=768 ; i < 1280 ; i++ ) + clampTbl[i] = 255; + +} +#endif + +/* + * Internal helpers + */ + +static inline int readAC( struct rqBitReader *br, int *run, int *amp ) +{ + /* Vars */ + unsigned int cod; + + /* Get 16 bits */ + cod = rqBR_peekBits(br,16); + + /* Lookup in the table */ + *run = vlcTbl_run[cod>>6]; + *amp = vlcTbl_amp[cod>>6]; + rqBR_flushBits(br,vlcTbl_len[cod>>6]); + + if (*amp > 0) { + + /* Normal stuff, just correct the sign */ + if (cod & (0x10000 >> vlcTbl_len[cod>>6])) + *amp = - *amp; + } else { + + /* Handle special cases */ + if (!*amp) + return 0; + if (*amp == -1) { + + /* 0100110srrraaaaa */ + *run = ( cod >> 5 ) & 0x07; + *amp = ( cod & 0x100) ? + -(cod&0x1F) : (cod&0x1F); + } else { + + /* 0100111srrrraaaa */ + *run = ( cod >> 4 ) & 0x0F; + *amp = ( cod & 0x100) ? + -(cod&0x0F) : (cod&0x0F); + } + } + + return 1; +} + + +#define iDCT_column(b0,b1,b2,b3) do { \ + int t0,t1,t2,t3; \ + \ + t0 = ( b1 + b3 ) << 5; \ + t2 = t0 - (b3 << 4); \ + t3 = (b1 * 47) - t0; \ + t0 = b0 + b2; \ + t1 = b0 - b2; \ + \ + b0 = ( t0 + t2 ); \ + b1 = ( t1 + t3 ); \ + b3 = ( t0 - t2 ); \ + b2 = ( t1 - t3 ); \ +} while (0) + +#define iDCT_line(b0,b1,b2,b3) do { \ + int t0,t1,t2,t3,bm0,bm2; \ + \ + bm0 = b0 << 7; \ + bm2 = b2 << 7; \ + \ + t0 = bm0 + bm2; \ + t1 = bm0 - bm2; \ + t2 = b1 * 183 + b3 * 86; \ + t3 = b1 * 86 - b3 * 183; \ + \ + b0 = ( t0 + t2 ) >> 22; \ + b1 = ( t1 + t3 ) >> 22; \ + b3 = ( t0 - t2 ) >> 22; \ + b2 = ( t1 - t3 ) >> 22; \ +} while (0) + +/* Decode a block + * Basic ops : get the DC - get the ACs - deZigZag - deWeighting - + * deQuantization - iDCT + * Here they are a little mixed-up to speed all this up. + */ +static inline int decodeBlock( struct rqBitReader *br, int *block, int *dc ) +{ + /* Tables used for block decoding */ + + /* deZigZag table + * + * ZigZag: each of the coefficient of the DCT transformed 4x4 + * matrix is taken in a certain order to make a linear + * array with the high frequency AC at the end + * + * / 0 1 5 6 \ . + * | 2 4 7 12 | This is the order taken. We must deZigZag + * | 3 8 11 13 | to reconstitute the original matrix + * \ 9 10 14 15 / + */ + static const int iZigZagTbl[16] = + { 0, 1, 4, 8, 5, 2, 3, 6, 9,12, 13, 10, 7, 11, 14, 15 }; + + /* deQuantization, deWeighting & iDCT premultiply */ + + /* + * Weighting : Each DCT coefficient is weighted by a certain factor. We + * must compensate for this to rebuilt the original DCT matrix. + * + * Quantization: According to the read Q factor, DCT coefficient are + * quantized. We need to compensate for this. + * + * iDCT premultiply: Since for the first iDCT pass ( column ), we'll need + * to do some multiplication, the ones that we can + * integrate here, we do. + * + * Rem: - The factors are here presented in the ZigZaged order, + * because we will need those BEFORE the deZigZag + * - For more informations, consult jpgl_tbl.c, it's the little + * prog that computes this table + */ + static const int iQWTbl[4][16] = { + { 32768, 17808, 794, 18618, 850, 18618, 43115, 1828, + 40960, 1924, 2089, 45511, 2089, 49648, 2216, 2521 }, + { 32768, 35617, 1589, 37236, 1700, 37236, 86231, 3656, + 81920, 3849, 4179, 91022, 4179, 99296, 4432, 5043 }, + { 32768, 71234, 3179, 74472, 3401, 74472, 172463, 7313, + 163840, 7698, 8358, 182044, 8358, 198593, 8865, 10087 }, + { 32768, 142469, 6359, 148945, 6803, 148945, 344926, 14627, + 327680, 15397, 16716, 364088, 16716, 397187, 17730, 20175 } + }; + + /* Vars */ + int hdr; + int *eff_iQWTbl; + int cc, run, amp; + + /* Read & Decode the block header ( Q, T, DC ) */ + hdr = rqBR_peekBits(br,11); + + if (hdr & 0x100) { + /* Differential mode */ + if (hdr & 0x80) + *dc += ( hdr >> 3 ) | ~0xF; + else + *dc += ( hdr >> 3 ) & 0xF; + + /* Flush the header bits */ + rqBR_flushBits(br,8); + } else { + /* Direct mode */ + if ( hdr & 0x80 ) + *dc = hdr | ~0x7F; + else + *dc = hdr & 0x7F; + + /* Flush the header bits */ + rqBR_flushBits(br,11); + } + + /* Clear the block & store DC ( with pre-multiply ) */ + block[0] = *dc << 15; + block[1] = 0x00; + block[2] = 0x00; + block[3] = 0x00; + block[4] = 0x00; + block[5] = 0x00; + block[6] = 0x00; + block[7] = 0x00; + block[8] = 0x00; + block[9] = 0x00; + block[10] = 0x00; + block[11] = 0x00; + block[12] = 0x00; + block[13] = 0x00; + block[14] = 0x00; + block[15] = 0x00; + + /* Read the AC coefficients + * at the same time, deZigZag, deQuantization, deWeighting & iDCT premultiply + */ + eff_iQWTbl = (int*) iQWTbl[hdr>>9]; + cc = 0; + + while ( readAC(br,&run,&) ) { + cc += run + 1; + if ( cc > 15 ) + return -1; + block[iZigZagTbl[cc]] = amp * eff_iQWTbl[cc]; + } + + /* Do the column iDCT ( what's left to do ) */ + iDCT_column(block[0], block[4], block[8], block[12]); + iDCT_column(block[1], block[5], block[9], block[13]); + iDCT_column(block[2], block[6], block[10], block[14]); + iDCT_column(block[3], block[7], block[11], block[15]); + + /* Do the line iDCT ( complete one here ) */ + iDCT_line(block[0], block[1], block[2], block[3]); + iDCT_line(block[4], block[5], block[6], block[7]); + iDCT_line(block[8], block[9], block[10], block[11]); + iDCT_line(block[12], block[13], block[14], block[15]); + + return !(hdr & 0x700); +} + +int v4lconvert_decode_jpgl(const unsigned char *inp, int src_size, + unsigned int dest_pix_fmt, unsigned char *fb, + int img_width, int img_height) +{ + /* Vars */ + struct RingQueue rq; + struct rqBitReader br; + + int row, col; /* Row & Column in the image */ + + int x,y; + int block_idx; + + unsigned char *Yline_baseptr, *Uline_baseptr, *Vline_baseptr; + unsigned char *Yline, *Uline, *Vline; + int Yline_baseofs, UVline_baseofs; + + int dc_y, dc_u, dc_v; /* DC Coefficients */ + int block_y[16*4]; /* Y blocks */ + int block_u[16]; /* U block */ + int block_v[16]; /* V block */ + + unsigned char *mainbuffer; + + int yc,uc,vc; + + /* init the decoder */ + if (yuvTbl_y[0] == 0) { + vlcTbl_init(); + yuvTbl_init(); +#ifndef SAFE_CLAMP + clampTbl_init(); +#endif + } + + img_height /= 4; + + /* Prepare a bit-by-bit reader */ + rq.queue = inp; + rq.length = src_size; + rq.ri = 0; + rqBR_init(&br, &rq); + + /* Allocate a big buffer & setup pointers */ + switch (dest_pix_fmt) { + default: +/* case V4L2_PIX_FMT_RGB24: */ +/* case V4L2_PIX_FMT_BGR24: */ + mainbuffer = malloc(4 * (img_width + (img_width >> 1) + 2)); + + Yline_baseptr = mainbuffer; + Uline_baseptr = mainbuffer + (4 * img_width); + Vline_baseptr = Uline_baseptr + (img_width + 4); + break; + case V4L2_PIX_FMT_YUV420: + mainbuffer = NULL; + Yline_baseptr = fb; + Uline_baseptr = fb + img_width * img_height * 16; + Vline_baseptr = Uline_baseptr + img_width * img_height * 4; + break; + case V4L2_PIX_FMT_YVU420: + mainbuffer = NULL; + Yline_baseptr = fb; + Vline_baseptr = fb + img_width * img_height * 16; + Uline_baseptr = Vline_baseptr + img_width * img_height * 4; + break; + } + + Yline_baseofs = img_width - 4; + UVline_baseofs = (img_width >> 2) - 3; + + /* Process 4 lines at a time ( one block height ) */ + for ( row=0 ; row> 2); + Vline = Vline_baseptr + (col >> 2); + + for ( y=0 ; y<4 ; y++) { + /* Scan line */ + for ( x=0 ; x<4 ; x++ ) { + /* Y block */ + Yline[ 0] = clamp_adjust(block_y[block_idx ]); + Yline[ 4] = clamp_adjust(block_y[block_idx+16]); + Yline[ 8] = clamp_adjust(block_y[block_idx+32]); + Yline[12] = clamp_adjust(block_y[block_idx+48]); + + /* U block */ + *Uline = clamp_adjust(block_u[block_idx]); + + /* V block */ + *Vline = clamp_adjust(block_v[block_idx]); + + /* Ajust pointers & index */ + block_idx++; + Yline++; + Uline++; + Vline++; + } + + /* Adjust pointers */ + Yline += Yline_baseofs; + Uline += UVline_baseofs; + Vline += UVline_baseofs; + } + } + + /* Handle interpolation special case ( at the end of the lines ) */ + Uline = Uline_baseptr + (UVline_baseofs+2); + Vline = Vline_baseptr + (UVline_baseofs+2); + for ( y=0 ; y<4 ; y++ ) { + /* Copy the last pixel */ + Uline[1] = Uline[0]; + Vline[1] = Vline[0]; + + /* Adjust ptr */ + Uline += UVline_baseofs+4; + Vline += UVline_baseofs+4; + } + + /* We have 4 complete lines, so tempbuffer -> framebuffer + * Go line by line */ + + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + Yline = Yline_baseptr; + Uline = Uline_baseptr; + Vline = Vline_baseptr; + for ( y=0 ; y<4 ; y++ ) { + /* Process 4 pixel at a time to handle interpolation + * for U & V values */ + for ( x=0 ; x> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + + /* Second pixel */ + yc = yuvTbl_y[*(Yline++)]; + uc = ( 3*Uline[0] + Uline[1] ) >> 2; + vc = ( 3*Vline[0] + Vline[1] ) >> 2; + + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + + /* Third pixel */ + yc = yuvTbl_y[*(Yline++)]; + uc = ( Uline[0] + Uline[1] ) >> 1; + vc = ( Vline[0] + Vline[1] ) >> 1; + + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + + /* Fourth pixel */ + yc = yuvTbl_y[*(Yline++)]; + uc = ( Uline[0] + 3*Uline[1] ) >> 2; + vc = ( Vline[0] + 3*Vline[1] ) >> 2; + + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + + /* Adjust pointers */ + Uline++; + Vline++; + } + + /* Adjust pointers */ + Uline++; + Vline++; + } + break; + case V4L2_PIX_FMT_BGR24: + Yline = Yline_baseptr; + Uline = Uline_baseptr; + Vline = Vline_baseptr; + for ( y=0 ; y<4 ; y++ ) { + /* Process 4 pixel at a time to handle interpolation + * for U & V values */ + for ( x=0 ; x> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + + /* Second pixel */ + yc = yuvTbl_y[*(Yline++)]; + uc = ( 3*Uline[0] + Uline[1] ) >> 2; + vc = ( 3*Vline[0] + Vline[1] ) >> 2; + + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + + /* Third pixel */ + yc = yuvTbl_y[*(Yline++)]; + uc = ( Uline[0] + Uline[1] ) >> 1; + vc = ( Vline[0] + Vline[1] ) >> 1; + + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + + /* Fourth pixel */ + yc = yuvTbl_y[*(Yline++)]; + uc = ( Uline[0] + 3*Uline[1] ) >> 2; + vc = ( Vline[0] + 3*Vline[1] ) >> 2; + + *(fb++) = clamp(( yc + yuvTbl_v2[vc] ) >> 16); + *(fb++) = clamp(( yc - yuvTbl_u2[uc] - yuvTbl_v1[vc] ) >> 16); + *(fb++) = clamp(( yc + yuvTbl_u1[uc] ) >> 16); + + /* Adjust pointers */ + Uline++; + Vline++; + } + + /* Adjust pointers */ + Uline++; + Vline++; + } + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + Yline_baseptr += img_width * 4; + Uline_baseptr += img_width; + Vline_baseptr += img_width; + break; + } + } + + /* Free our buffer */ + if (mainbuffer != NULL) + free(mainbuffer); + + return 0; +} + +/* +Return-Path: tnt@246tNt.com +Received: from zimbra16-e3.priv.proxad.net (LHLO + zimbra16-e3.priv.proxad.net) (172.20.243.166) by + zimbra16-e3.priv.proxad.net with LMTP; Mon, 14 Feb 2011 21:10:38 +0100 + (CET) +Received: from mailrelay011.isp.belgacom.be (mx26-g26.priv.proxad.net [172.20.243.96]) + by zimbra16-e3.priv.proxad.net (Postfix) with ESMTP id 1A661157C5B + for ; Mon, 14 Feb 2011 21:10:38 +0100 (CET) +Received: from mailrelay011.isp.belgacom.be ([195.238.6.178]) + by mx1-g20.free.fr (MXproxy) for moinejf@free.fr ; + Mon, 14 Feb 2011 21:10:36 +0100 (CET) +X-ProXaD-SC: state=HAM score=0 +X-Belgacom-Dynamic: yes +X-IronPort-Anti-Spam-Filtered: true +X-IronPort-Anti-Spam-Result: ApIBAKsaWU1XQ5W2/2dsb2JhbAAMhBHOEpA5gSeBaYFYdgSLfw +Received: from 182.149-67-87.adsl-dyn.isp.belgacom.be (HELO [10.0.0.129]) ([87.67.149.182]) + by relay.skynet.be with ESMTP; 14 Feb 2011 21:10:36 +0100 +Message-ID: <4D598C7C.7080307@246tNt.com> +Date: Mon, 14 Feb 2011 21:11:40 +0100 +From: Sylvain Munaut +User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101219 Lightning/1.0b3pre Thunderbird/3.1.7 +MIME-Version: 1.0 +To: Jean-Francois Moine +CC: Kjell Claesson +Subject: Re: nw80x as a gspca subdriv +References: <20110209204208.4b19df88@tele> <4D53B3BF.9050908@246tNt.com> <20110214205107.18c29303@tele> +In-Reply-To: <20110214205107.18c29303@tele> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 7bit + + [snip] +> May I have your permission to relicense your JPEG Lite decompression +> code under the LGPL (version 2 or later)? + +Yes, sure. + +""" +I hereby allow the nw80x driver code, including the jpeg lite decoding +routines, to be used and distributed under the LGPL v2 or later. +""" + [snip] +Cheers, + + Sylvain + */ diff --git a/libv4lconvert/libv4lconvert-priv.h b/libv4lconvert/libv4lconvert-priv.h new file mode 100644 index 0000000..9a1344d --- /dev/null +++ b/libv4lconvert/libv4lconvert-priv.h @@ -0,0 +1,283 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifndef __LIBV4LCONVERT_PRIV_H +#define __LIBV4LCONVERT_PRIV_H + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#ifdef HAVE_JPEG +#include +#endif +#include +#include "libv4l-plugin.h" +#include "libv4lconvert.h" +#include "control/libv4lcontrol.h" +#include "processing/libv4lprocessing.h" +#include "tinyjpeg.h" + +#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) + +#define V4LCONVERT_ERROR_MSG_SIZE 256 +#define V4LCONVERT_MAX_FRAMESIZES 256 + +#define V4LCONVERT_ERR(...) \ + snprintf(data->error_msg, V4LCONVERT_ERROR_MSG_SIZE, \ + "v4l-convert: error " __VA_ARGS__) + +/* Card flags */ +#define V4LCONVERT_IS_UVC 0x01 +#define V4LCONVERT_USE_TINYJPEG 0x02 + +struct v4lconvert_data { + int fd; + int flags; /* bitfield */ + int control_flags; /* bitfield */ + unsigned int no_formats; + int64_t supported_src_formats; /* bitfield */ + char error_msg[V4LCONVERT_ERROR_MSG_SIZE]; + struct jdec_private *tinyjpeg; +#ifdef HAVE_JPEG + struct jpeg_error_mgr jerr; + int jerr_errno; + jmp_buf jerr_jmp_state; + struct jpeg_decompress_struct cinfo; + int cinfo_initialized; +#endif // HAVE_JPEG + struct v4l2_frmsizeenum framesizes[V4LCONVERT_MAX_FRAMESIZES]; + unsigned int no_framesizes; + int bandwidth; + int fps; + int convert1_buf_size; + int convert2_buf_size; + int rotate90_buf_size; + int flip_buf_size; + int convert_pixfmt_buf_size; + unsigned char *convert1_buf; + unsigned char *convert2_buf; + unsigned char *rotate90_buf; + unsigned char *flip_buf; + unsigned char *convert_pixfmt_buf; + struct v4lcontrol_data *control; + struct v4lprocessing_data *processing; + void *dev_ops_priv; + const struct libv4l_dev_ops *dev_ops; + + /* Data for external decompression helpers code */ + pid_t decompress_pid; + int decompress_in_pipe[2]; /* Data from helper to us */ + int decompress_out_pipe[2]; /* Data from us to helper */ + + /* For mr97310a decoder */ + int frames_dropped; + + /* For cpia1 decoder */ + unsigned char *previous_frame; +}; + +struct v4lconvert_pixfmt { + unsigned int fmt; /* v4l2 fourcc */ + int bpp; /* bits per pixel, 0 for compressed formats */ + int rgb_rank; /* rank for converting to rgb32 / bgr32 */ + int yuv_rank; /* rank for converting to yuv420 / yvu420 */ + int needs_conversion; +}; + +unsigned char *v4lconvert_alloc_buffer(int needed, + unsigned char **buf, int *buf_size); + +int v4lconvert_oom_error(struct v4lconvert_data *data); + +void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int bgr, int yvu, int bpp); + +void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + +void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + +void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dst, + int width, int height, int stride); + +void v4lconvert_yuyv_to_bgr24(const unsigned char *src, unsigned char *dst, + int width, int height, int stride); + +void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int stride, int yvu); + +void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst, + int width, int height, int stride); + +void v4lconvert_yvyu_to_bgr24(const unsigned char *src, unsigned char *dst, + int width, int height, int stride); + +void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dst, + int width, int height, int stride); + +void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dst, + int width, int height, int stride); + +void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int stride, int yvu); + +void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dst, + const struct v4l2_format *src_fmt); + +void v4lconvert_grey_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_grey_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt); + +void v4lconvert_y16_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int little_endian); + +void v4lconvert_y16_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int little_endian); + +void v4lconvert_rgb32_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int bgr); + +int v4lconvert_y10b_to_rgb24(struct v4lconvert_data *data, + const unsigned char *src, unsigned char *dest, int width, int height); + +int v4lconvert_y10b_to_yuv420(struct v4lconvert_data *data, + const unsigned char *src, unsigned char *dest, int width, int height); + +void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height); + +void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int yvu); + +void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + +void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + +void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + +void v4lconvert_cit_yyvyuy_to_yuv420(const unsigned char *src, + unsigned char *ydest, + int width, int height, int yvu); + +void v4lconvert_konica_yuv420_to_yuv420(const unsigned char *src, + unsigned char *ydest, + int width, int height, int yvu); + +void v4lconvert_m420_to_yuv420(const unsigned char *src, + unsigned char *ydest, + int width, int height, int yvu); + +int v4lconvert_cpia1_to_yuv420(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dst, int width, int height, int yvu); + +void v4lconvert_sn9c20x_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu); + +int v4lconvert_se401_to_rgb24(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height); + +int v4lconvert_decode_jpeg_tinyjpeg(struct v4lconvert_data *data, + unsigned char *src, int src_size, unsigned char *dest, + struct v4l2_format *fmt, unsigned int dest_pix_fmt, int flags); + +int v4lconvert_decode_jpeg_libjpeg(struct v4lconvert_data *data, + unsigned char *src, int src_size, unsigned char *dest, + struct v4l2_format *fmt, unsigned int dest_pix_fmt); + +int v4lconvert_decode_jpgl(const unsigned char *src, int src_size, + unsigned int dest_pix_fmt, unsigned char *dest, int width, int height); + +void v4lconvert_decode_spca561(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_decode_sn9c10x(const unsigned char *src, unsigned char *dst, + int width, int height); + +int v4lconvert_decode_pac207(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, unsigned char *outp, + int width, int height); + +int v4lconvert_decode_mr97310a(struct v4lconvert_data *data, + const unsigned char *src, int src_size, unsigned char *dst, + int width, int height); + +int v4lconvert_decode_jl2005bcd(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height); + +void v4lconvert_decode_sn9c2028(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_decode_stv0680(const unsigned char *src, unsigned char *dst, + int width, int height); + +void v4lconvert_bayer_to_rgb24(const unsigned char *bayer, + unsigned char *rgb, int width, int height, const unsigned int stride, unsigned int pixfmt); + +void v4lconvert_bayer_to_bgr24(const unsigned char *bayer, + unsigned char *rgb, int width, int height, const unsigned int stride, unsigned int pixfmt); + +void v4lconvert_bayer_to_yuv420(const unsigned char *bayer, unsigned char *yuv, + int width, int height, const unsigned int stride, unsigned int src_pixfmt, int yvu); + +void v4lconvert_hm12_to_rgb24(const unsigned char *src, + unsigned char *dst, int width, int height); + +void v4lconvert_hm12_to_bgr24(const unsigned char *src, + unsigned char *dst, int width, int height); + +void v4lconvert_hm12_to_yuv420(const unsigned char *src, + unsigned char *dst, int width, int height, int yvu); + +void v4lconvert_rotate90(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt); + +void v4lconvert_flip(unsigned char *src, unsigned char *dest, + struct v4l2_format *fmt, int hflip, int vflip); + +void v4lconvert_crop(unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, const struct v4l2_format *dest_fmt); + +int v4lconvert_helper_decompress(struct v4lconvert_data *data, + const char *helper, const unsigned char *src, int src_size, + unsigned char *dest, int dest_size, int width, int height, int command); + +void v4lconvert_helper_cleanup(struct v4lconvert_data *data); + +#endif diff --git a/libv4lconvert/libv4lconvert.c b/libv4lconvert/libv4lconvert.c new file mode 100644 index 0000000..8562a6d --- /dev/null +++ b/libv4lconvert/libv4lconvert.c @@ -0,0 +1,1701 @@ +/* +# (C) 2008-2011 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifdef ANDROID +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include "libv4lconvert.h" +#include "libv4lconvert-priv.h" +#include "libv4lsyscall-priv.h" + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +static void *dev_init(int fd) +{ + return NULL; +} + +static void dev_close(void *dev_ops_priv) +{ +} + +static int dev_ioctl(void *dev_ops_priv, int fd, unsigned long cmd, void *arg) +{ + return SYS_IOCTL(fd, cmd, arg); +} + +static ssize_t dev_read(void *dev_ops_priv, int fd, void *buf, size_t len) +{ + return SYS_READ(fd, buf, len); +} + +static ssize_t dev_write(void *dev_ops_priv, int fd, const void *buf, + size_t len) +{ + return SYS_WRITE(fd, buf, len); +} + +static const struct libv4l_dev_ops default_dev_ops = { + .init = dev_init, + .close = dev_close, + .ioctl = dev_ioctl, + .read = dev_read, + .write = dev_write, +}; + +const struct libv4l_dev_ops *v4lconvert_get_default_dev_ops() +{ + return &default_dev_ops; +} + +static void v4lconvert_get_framesizes(struct v4lconvert_data *data, + unsigned int pixelformat, int index); + +/* Note for proper functioning of v4lconvert_enum_fmt the first entries in + supported_src_pixfmts must match with the entries in supported_dst_pixfmts */ +#define SUPPORTED_DST_PIXFMTS \ + /* fourcc bpp rgb yuv needs */ \ + /* rank rank conversion */ \ + { V4L2_PIX_FMT_RGB24, 24, 1, 5, 0 }, \ + { V4L2_PIX_FMT_BGR24, 24, 1, 5, 0 }, \ + { V4L2_PIX_FMT_YUV420, 12, 6, 1, 0 }, \ + { V4L2_PIX_FMT_YVU420, 12, 6, 1, 0 } + +static const struct v4lconvert_pixfmt supported_src_pixfmts[] = { + SUPPORTED_DST_PIXFMTS, + /* packed rgb formats */ + { V4L2_PIX_FMT_RGB565, 16, 4, 6, 0 }, + { V4L2_PIX_FMT_BGR32, 32, 4, 6, 0 }, + { V4L2_PIX_FMT_RGB32, 32, 4, 6, 0 }, + { V4L2_PIX_FMT_XBGR32, 32, 4, 6, 0 }, + { V4L2_PIX_FMT_XRGB32, 32, 4, 6, 0 }, + { V4L2_PIX_FMT_ABGR32, 32, 4, 6, 0 }, + { V4L2_PIX_FMT_ARGB32, 32, 4, 6, 0 }, + /* yuv 4:2:2 formats */ + { V4L2_PIX_FMT_YUYV, 16, 5, 4, 0 }, + { V4L2_PIX_FMT_YVYU, 16, 5, 4, 0 }, + { V4L2_PIX_FMT_UYVY, 16, 5, 4, 0 }, + /* yuv 4:2:0 formats */ + { V4L2_PIX_FMT_SPCA501, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_SPCA505, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_SPCA508, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_CIT_YYVYUY, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_KONICA420, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_SN9C20X_I420, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_M420, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_HM12, 12, 6, 3, 1 }, + { V4L2_PIX_FMT_CPIA1, 0, 6, 3, 1 }, + /* JPEG and variants */ + { V4L2_PIX_FMT_MJPEG, 0, 7, 7, 0 }, + { V4L2_PIX_FMT_JPEG, 0, 7, 7, 0 }, + { V4L2_PIX_FMT_PJPG, 0, 7, 7, 1 }, + { V4L2_PIX_FMT_JPGL, 0, 7, 7, 1 }, + { V4L2_PIX_FMT_OV511, 0, 7, 7, 1 }, + { V4L2_PIX_FMT_OV518, 0, 7, 7, 1 }, + /* uncompressed bayer */ + { V4L2_PIX_FMT_SBGGR8, 8, 8, 8, 1 }, + { V4L2_PIX_FMT_SGBRG8, 8, 8, 8, 1 }, + { V4L2_PIX_FMT_SGRBG8, 8, 8, 8, 1 }, + { V4L2_PIX_FMT_SRGGB8, 8, 8, 8, 1 }, + { V4L2_PIX_FMT_STV0680, 8, 8, 8, 1 }, + /* compressed bayer */ + { V4L2_PIX_FMT_SPCA561, 0, 9, 9, 1 }, + { V4L2_PIX_FMT_SN9C10X, 0, 9, 9, 1 }, + { V4L2_PIX_FMT_SN9C2028, 0, 9, 9, 1 }, + { V4L2_PIX_FMT_PAC207, 0, 9, 9, 1 }, + { V4L2_PIX_FMT_MR97310A, 0, 9, 9, 1 }, +#ifdef HAVE_JPEG + { V4L2_PIX_FMT_JL2005BCD, 0, 9, 9, 1 }, +#endif + { V4L2_PIX_FMT_SQ905C, 0, 9, 9, 1 }, + /* special */ + { V4L2_PIX_FMT_SE401, 0, 8, 9, 1 }, + /* grey formats */ + { V4L2_PIX_FMT_GREY, 8, 20, 20, 0 }, + { V4L2_PIX_FMT_Y4, 8, 20, 20, 0 }, + { V4L2_PIX_FMT_Y6, 8, 20, 20, 0 }, + { V4L2_PIX_FMT_Y10BPACK, 10, 20, 20, 0 }, + { V4L2_PIX_FMT_Y16, 16, 20, 20, 0 }, + { V4L2_PIX_FMT_Y16_BE, 16, 20, 20, 0 }, +}; + +static const struct v4lconvert_pixfmt supported_dst_pixfmts[] = { + SUPPORTED_DST_PIXFMTS +}; + +/* List of well known resolutions which we can get by cropping somewhat larger + resolutions */ +static const unsigned int v4lconvert_crop_res[][2] = { + /* low res VGA resolutions, can be made by software cropping SIF resolutions + for cam/drivers which do not support this in hardware */ + { 320, 240 }, + { 160, 120 }, + /* Some CIF cams (with vv6410 sensor) have slightly larger then usual CIF + resolutions, make regular CIF resolutions available on these by sw crop */ + { 352, 288 }, + { 176, 144 }, +}; + +struct v4lconvert_data *v4lconvert_create(int fd) +{ + return v4lconvert_create_with_dev_ops(fd, NULL, &default_dev_ops); +} + +struct v4lconvert_data *v4lconvert_create_with_dev_ops(int fd, void *dev_ops_priv, + const struct libv4l_dev_ops *dev_ops) +{ + int i, j; + struct v4lconvert_data *data = calloc(1, sizeof(struct v4lconvert_data)); + struct v4l2_capability cap; + /* This keeps tracks of devices which have only formats for which apps + most likely will need conversion and we can thus safely add software + processing controls without a performance impact. */ + int always_needs_conversion = 1; + + if (!data) { + fprintf(stderr, "libv4lconvert: error: out of memory!\n"); + return NULL; + } + + data->fd = fd; + data->dev_ops = dev_ops; + data->dev_ops_priv = dev_ops_priv; + data->decompress_pid = -1; + data->fps = 30; + + /* Check supported formats */ + for (i = 0; ; i++) { + struct v4l2_fmtdesc fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE }; + + fmt.index = i; + + if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUM_FMT, &fmt)) + break; + + for (j = 0; j < ARRAY_SIZE(supported_src_pixfmts); j++) + if (fmt.pixelformat == supported_src_pixfmts[j].fmt) + break; + + if (j < ARRAY_SIZE(supported_src_pixfmts)) { + data->supported_src_formats |= 1ULL << j; + v4lconvert_get_framesizes(data, fmt.pixelformat, j); + if (!supported_src_pixfmts[j].needs_conversion) + always_needs_conversion = 0; + } else + always_needs_conversion = 0; + } + + data->no_formats = i; + + /* Check if this cam has any special flags */ + if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_QUERYCAP, &cap) == 0) { + if (!strcmp((char *)cap.driver, "uvcvideo")) + data->flags |= V4LCONVERT_IS_UVC; + + if (cap.capabilities & V4L2_CAP_DEVICE_CAPS) + cap.capabilities = cap.device_caps; + if ((cap.capabilities & 0xff) & ~V4L2_CAP_VIDEO_CAPTURE) + always_needs_conversion = 0; + } + + data->control = v4lcontrol_create(fd, dev_ops_priv, dev_ops, + always_needs_conversion); + if (!data->control) { + free(data); + return NULL; + } + data->bandwidth = v4lcontrol_get_bandwidth(data->control); + data->control_flags = v4lcontrol_get_flags(data->control); + if (data->control_flags & V4LCONTROL_FORCE_TINYJPEG) + data->flags |= V4LCONVERT_USE_TINYJPEG; + + data->processing = v4lprocessing_create(fd, data->control); + if (!data->processing) { + v4lcontrol_destroy(data->control); + free(data); + return NULL; + } + + return data; +} + +void v4lconvert_destroy(struct v4lconvert_data *data) +{ + if (!data) + return; + + v4lprocessing_destroy(data->processing); + v4lcontrol_destroy(data->control); + if (data->tinyjpeg) { + unsigned char *comps[3] = { NULL, NULL, NULL }; + + tinyjpeg_set_components(data->tinyjpeg, comps, 3); + tinyjpeg_free(data->tinyjpeg); + } +#ifdef HAVE_JPEG + if (data->cinfo_initialized) + jpeg_destroy_decompress(&data->cinfo); +#endif // HAVE_JPEG + v4lconvert_helper_cleanup(data); + free(data->convert1_buf); + free(data->convert2_buf); + free(data->rotate90_buf); + free(data->flip_buf); + free(data->convert_pixfmt_buf); + free(data->previous_frame); + free(data); +} + +int v4lconvert_supported_dst_format(unsigned int pixelformat) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) + if (supported_dst_pixfmts[i].fmt == pixelformat) + break; + + return i != ARRAY_SIZE(supported_dst_pixfmts); +} + +int v4lconvert_supported_dst_fmt_only(struct v4lconvert_data *data) +{ + return v4lcontrol_needs_conversion(data->control) && + data->supported_src_formats; +} + +/* See libv4lconvert.h for description of in / out parameters */ +int v4lconvert_enum_fmt(struct v4lconvert_data *data, struct v4l2_fmtdesc *fmt) +{ + int i, no_faked_fmts = 0; + unsigned int faked_fmts[ARRAY_SIZE(supported_dst_pixfmts)]; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + (!v4lconvert_supported_dst_fmt_only(data) && + fmt->index < data->no_formats)) + return data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUM_FMT, fmt); + + for (i = 0; i < ARRAY_SIZE(supported_dst_pixfmts); i++) + if (v4lconvert_supported_dst_fmt_only(data) || + !(data->supported_src_formats & (1ULL << i))) { + faked_fmts[no_faked_fmts] = supported_dst_pixfmts[i].fmt; + no_faked_fmts++; + } + + if (!v4lconvert_supported_dst_fmt_only(data)) + i = fmt->index - data->no_formats; + else + i = fmt->index; + + if (i >= no_faked_fmts) { + errno = EINVAL; + return -1; + } + + fmt->flags = V4L2_FMT_FLAG_EMULATED; + fmt->pixelformat = faked_fmts[i]; + fmt->description[0] = faked_fmts[i] & 0xff; + fmt->description[1] = (faked_fmts[i] >> 8) & 0xff; + fmt->description[2] = (faked_fmts[i] >> 16) & 0xff; + fmt->description[3] = faked_fmts[i] >> 24; + fmt->description[4] = '\0'; + memset(fmt->reserved, 0, sizeof(fmt->reserved)); + + return 0; +} + +/* This function returns a value to rank (sort) source format by preference + when multiple source formats are available for a certain resolution, the + source format for which this function returns the lowest value wins. + + This function uses the rgb_rank resp. yuv_rank values as a base when + converting to rgb32 resp. yuv420. The initial ranks range from 1 - 10, + the initial rank purely expresses the CPU cost of doing the conversion, the + ranking algorithm will give a penalty of 10 points if + (width * height * fps * bpp / 8) > bandwidth + thus disqualifying a src format which causes the bandwidth to be exceeded, + except when all of them cause this. + + Note grey scale formats start at 20 rather then 1-10, because we want to + never autoselect them, unless they are the only choice */ +static int v4lconvert_get_rank(struct v4lconvert_data *data, + int src_index, int src_width, int src_height, + unsigned int dest_pixelformat) +{ + int needed, rank = 0; + + switch (dest_pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + rank = supported_src_pixfmts[src_index].rgb_rank; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + rank = supported_src_pixfmts[src_index].yuv_rank; + break; + } + + /* So that if both rgb32 and bgr32 are supported, or both yuv420 and + yvu420 the right one wins */ + if (supported_src_pixfmts[src_index].fmt == dest_pixelformat) + rank--; + + /* check bandwidth needed */ + needed = src_width * src_height * data->fps * + supported_src_pixfmts[src_index].bpp / 8; + if (data->bandwidth && needed > data->bandwidth) + rank += 10; +#if 0 + printf("ranked: %c%c%c%c for %dx%d @ %d fps, needed: %d, bandwidth: %d, rank: %d\n", + supported_src_pixfmts[src_index].fmt & 0xff, + (supported_src_pixfmts[src_index].fmt >> 8) & 0xff, + (supported_src_pixfmts[src_index].fmt >> 16) & 0xff, + supported_src_pixfmts[src_index].fmt >> 24, src_width, + src_height, data->fps, needed, data->bandwidth, rank); +#endif + return rank; +} + +/* Find out what format to use based on the (cached) results of enum + framesizes instead of doing a zillion try_fmt calls. This function + currently is intended for use with UVC cams only. This is esp. + important for UVC based cams as doing try_fmt there actually causes I/O */ +static int v4lconvert_do_try_format_uvc(struct v4lconvert_data *data, + struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt) +{ + unsigned int i, rank; + unsigned int closest_fmt_size_diff = -1; + int best_framesize = 0;/* Just use the first format if no small enough one */ + int best_format = 0; + unsigned int best_rank = 100; + + for (i = 0; i < data->no_framesizes; i++) { + if (data->framesizes[i].discrete.width <= dest_fmt->fmt.pix.width && + data->framesizes[i].discrete.height <= dest_fmt->fmt.pix.height) { + int size_x_diff = dest_fmt->fmt.pix.width - + data->framesizes[i].discrete.width; + int size_y_diff = dest_fmt->fmt.pix.height - + data->framesizes[i].discrete.height; + unsigned int size_diff = size_x_diff * size_x_diff + + size_y_diff * size_y_diff; + + if (size_diff < closest_fmt_size_diff) { + closest_fmt_size_diff = size_diff; + best_framesize = i; + } + } + } + + for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) { + /* is this format supported? */ + if (!(data->framesizes[best_framesize].pixel_format & (1 << i))) + continue; + + /* Note the hardcoded use of discrete is based on this function + only getting called for uvc devices */ + rank = v4lconvert_get_rank(data, i, + data->framesizes[best_framesize].discrete.width, + data->framesizes[best_framesize].discrete.height, + dest_fmt->fmt.pix.pixelformat); + if (rank < best_rank) { + best_rank = rank; + best_format = supported_src_pixfmts[i].fmt; + } + } + + dest_fmt->fmt.pix.width = data->framesizes[best_framesize].discrete.width; + dest_fmt->fmt.pix.height = data->framesizes[best_framesize].discrete.height; + dest_fmt->fmt.pix.field = V4L2_FIELD_NONE; /* UVC has no fields */ + /* Not pretty, the pwc driver doesn't fill these in try_fmt either though, + so we should be able to get away with this. */ + dest_fmt->fmt.pix.bytesperline = 0; + dest_fmt->fmt.pix.sizeimage = 0; + dest_fmt->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT; + dest_fmt->fmt.pix.priv = 0; + + *src_fmt = *dest_fmt; + src_fmt->fmt.pix.pixelformat = best_format; + + return 0; +} + +static int v4lconvert_do_try_format(struct v4lconvert_data *data, + struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt) +{ + int i, size_x_diff, size_y_diff, rank, best_rank = 0; + unsigned int size_diff, closest_fmt_size_diff = -1; + unsigned int desired_pixfmt = dest_fmt->fmt.pix.pixelformat; + struct v4l2_format try_fmt, closest_fmt = { .type = 0 }; + + if (data->flags & V4LCONVERT_IS_UVC) + return v4lconvert_do_try_format_uvc(data, dest_fmt, src_fmt); + + for (i = 0; i < ARRAY_SIZE(supported_src_pixfmts); i++) { + /* is this format supported? */ + if (!(data->supported_src_formats & (1ULL << i))) + continue; + + try_fmt = *dest_fmt; + try_fmt.fmt.pix.pixelformat = supported_src_pixfmts[i].fmt; + if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_TRY_FMT, &try_fmt)) + continue; + + if (try_fmt.fmt.pix.pixelformat != + supported_src_pixfmts[i].fmt) + continue; + + /* Did we get a better match than before? */ + size_x_diff = (int)try_fmt.fmt.pix.width - + (int)dest_fmt->fmt.pix.width; + size_y_diff = (int)try_fmt.fmt.pix.height - + (int)dest_fmt->fmt.pix.height; + size_diff = size_x_diff * size_x_diff + + size_y_diff * size_y_diff; + + rank = v4lconvert_get_rank(data, i, + try_fmt.fmt.pix.width, + try_fmt.fmt.pix.height, + desired_pixfmt); + if (size_diff < closest_fmt_size_diff || + (size_diff == closest_fmt_size_diff && rank < best_rank)) { + closest_fmt = try_fmt; + closest_fmt_size_diff = size_diff; + best_rank = rank; + } + } + + if (closest_fmt.type == 0) + return -1; + + *dest_fmt = closest_fmt; + if (closest_fmt.fmt.pix.pixelformat != desired_pixfmt) + dest_fmt->fmt.pix.pixelformat = desired_pixfmt; + *src_fmt = closest_fmt; + + return 0; +} + +void v4lconvert_fixup_fmt(struct v4l2_format *fmt) +{ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * 3; + fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + fmt->fmt.pix.bytesperline = fmt->fmt.pix.width; + fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2; + break; + } +} + +/* See libv4lconvert.h for description of in / out parameters */ +int v4lconvert_try_format(struct v4lconvert_data *data, + struct v4l2_format *dest_fmt, struct v4l2_format *src_fmt) +{ + int i, result; + unsigned int desired_width = dest_fmt->fmt.pix.width; + unsigned int desired_height = dest_fmt->fmt.pix.height; + struct v4l2_format try_src, try_dest, try2_src, try2_dest; + + if (dest_fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + v4lconvert_supported_dst_fmt_only(data) && + !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) + dest_fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + + try_dest = *dest_fmt; + + /* Can we do conversion to the requested format & type? */ + if (!v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat) || + dest_fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + v4lconvert_do_try_format(data, &try_dest, &try_src)) { + result = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_TRY_FMT, dest_fmt); + if (src_fmt) + *src_fmt = *dest_fmt; + return result; + } + + /* In case of a non exact resolution match, try again with a slightly larger + resolution as some weird devices are not able to crop of the number of + extra (border) pixels most sensors have compared to standard resolutions, + which we will then just crop off in software */ + if (try_dest.fmt.pix.width != desired_width || + try_dest.fmt.pix.height != desired_height) { + try2_dest = *dest_fmt; + try2_dest.fmt.pix.width = desired_width + 7; + try2_dest.fmt.pix.height = desired_height + 1; + result = v4lconvert_do_try_format(data, &try2_dest, &try2_src); + if (result == 0 && + try2_dest.fmt.pix.width >= desired_width && + try2_dest.fmt.pix.width <= desired_width + 7 && + try2_dest.fmt.pix.height >= desired_height && + try2_dest.fmt.pix.height <= desired_height + 1) { + /* Success! */ + try2_dest.fmt.pix.width = desired_width; + try2_dest.fmt.pix.height = desired_height; + try_dest = try2_dest; + try_src = try2_src; + } + } + + /* In case of a non exact resolution match, see if this is a well known + resolution some apps are hardcoded too and try to give the app what it + asked for by cropping a slightly larger resolution or adding a small + black border to a slightly smaller resolution */ + if (try_dest.fmt.pix.width != desired_width || + try_dest.fmt.pix.height != desired_height) { + for (i = 0; i < ARRAY_SIZE(v4lconvert_crop_res); i++) { + if (v4lconvert_crop_res[i][0] == desired_width && + v4lconvert_crop_res[i][1] == desired_height) { + try2_dest = *dest_fmt; + + /* Note these are chosen so that cropping to vga res just works for + vv6410 sensor cams, which have 356x292 and 180x148 */ + try2_dest.fmt.pix.width = desired_width * 113 / 100; + try2_dest.fmt.pix.height = desired_height * 124 / 100; + result = v4lconvert_do_try_format(data, &try2_dest, &try2_src); + if (result == 0 && + (/* Add a small black border of max 16 pixels */ + (try2_dest.fmt.pix.width >= desired_width - 16 && + try2_dest.fmt.pix.width <= desired_width && + try2_dest.fmt.pix.height >= desired_height - 16 && + try2_dest.fmt.pix.height <= desired_height) || + /* Standard cropping to max 80% of actual width / height */ + (try2_dest.fmt.pix.width >= desired_width && + try2_dest.fmt.pix.width <= desired_width * 5 / 4 && + try2_dest.fmt.pix.height >= desired_height && + try2_dest.fmt.pix.height <= desired_height * 5 / 4) || + /* Downscale 2x + cropping to max 80% of actual width / height */ + (try2_dest.fmt.pix.width >= desired_width * 2 && + try2_dest.fmt.pix.width <= desired_width * 5 / 2 && + try2_dest.fmt.pix.height >= desired_height * 2 && + try2_dest.fmt.pix.height <= desired_height * 5 / 2))) { + /* Success! */ + try2_dest.fmt.pix.width = desired_width; + try2_dest.fmt.pix.height = desired_height; + try_dest = try2_dest; + try_src = try2_src; + } + break; + } + } + } + + /* Some applications / libs (*cough* gstreamer *cough*) will not work + correctly with planar YUV formats when the width is not a multiple of 8 + or the height is not a multiple of 2. With RGB formats these apps require + the width to be a multiple of 4. We apply the same rounding to all + formats to not end up with 2 close but different resolutions. */ + try_dest.fmt.pix.width &= ~7; + try_dest.fmt.pix.height &= ~1; + + /* Are we converting / cropping ? */ + if (try_src.fmt.pix.width != try_dest.fmt.pix.width || + try_src.fmt.pix.height != try_dest.fmt.pix.height || + try_src.fmt.pix.pixelformat != try_dest.fmt.pix.pixelformat) + v4lconvert_fixup_fmt(&try_dest); + + *dest_fmt = try_dest; + if (src_fmt) + *src_fmt = try_src; + + return 0; +} + +/* Is conversion necessary ? */ +int v4lconvert_needs_conversion(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt) /* in */ +{ + if (src_fmt->fmt.pix.width != dest_fmt->fmt.pix.width || + src_fmt->fmt.pix.height != dest_fmt->fmt.pix.height || + src_fmt->fmt.pix.pixelformat != dest_fmt->fmt.pix.pixelformat || + (v4lcontrol_needs_conversion(data->control) && + v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat))) + return 1; + + return 0; +} + +static int v4lconvert_processing_needs_double_conversion( + unsigned int src_pix_fmt, unsigned int dest_pix_fmt) +{ + switch (src_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_SPCA561: + case V4L2_PIX_FMT_SN9C10X: + case V4L2_PIX_FMT_PAC207: + case V4L2_PIX_FMT_MR97310A: +#ifdef HAVE_JPEG + case V4L2_PIX_FMT_JL2005BCD: +#endif + case V4L2_PIX_FMT_SN9C2028: + case V4L2_PIX_FMT_SQ905C: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_STV0680: + return 0; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return 0; + } + + return 1; +} + +unsigned char *v4lconvert_alloc_buffer(int needed, + unsigned char **buf, int *buf_size) +{ + if (*buf_size < needed) { + free(*buf); + *buf = malloc(needed); + if (*buf == NULL) { + *buf_size = 0; + return NULL; + } + *buf_size = needed; + } + return *buf; +} + +int v4lconvert_oom_error(struct v4lconvert_data *data) +{ + V4LCONVERT_ERR("could not allocate memory\n"); + errno = ENOMEM; + return -1; +} + +static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, + unsigned char *src, unsigned int src_size, unsigned char *dest, int dest_size, + struct v4l2_format *fmt, unsigned int dest_pix_fmt) +{ + int result = 0; + unsigned int src_pix_fmt = fmt->fmt.pix.pixelformat; + unsigned int width = fmt->fmt.pix.width; + unsigned int height = fmt->fmt.pix.height; + unsigned int bytesperline = fmt->fmt.pix.bytesperline; + + switch (src_pix_fmt) { + /* JPG and variants */ + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_JPEG: +#ifdef HAVE_JPEG + if (data->flags & V4LCONVERT_USE_TINYJPEG) { +#endif // HAVE_JPEG + result = v4lconvert_decode_jpeg_tinyjpeg(data, + src, src_size, dest, + fmt, dest_pix_fmt, 0); +#ifdef HAVE_JPEG + } else { + result = v4lconvert_decode_jpeg_libjpeg(data, + src, src_size, dest, + fmt, dest_pix_fmt); + if (result == -1 && errno == EOPNOTSUPP) { + /* Fall back to tinyjpeg */ + jpeg_destroy_decompress(&data->cinfo); + data->cinfo_initialized = 0; + data->flags |= V4LCONVERT_USE_TINYJPEG; + result = v4lconvert_decode_jpeg_tinyjpeg(data, + src, src_size, dest, + fmt, dest_pix_fmt, 0); + } + } +#endif // HAVE_JPEG + break; + case V4L2_PIX_FMT_PJPG: + result = v4lconvert_decode_jpeg_tinyjpeg(data, src, src_size, + dest, fmt, dest_pix_fmt, + TINYJPEG_FLAGS_PIXART_JPEG); + break; + case V4L2_PIX_FMT_JPGL: + result = v4lconvert_decode_jpgl(src, src_size, dest_pix_fmt, + dest, width, height); + break; + + /* Custom cam specific YUV formats */ + case V4L2_PIX_FMT_SPCA501: + case V4L2_PIX_FMT_SPCA505: + case V4L2_PIX_FMT_SPCA508: + case V4L2_PIX_FMT_CIT_YYVYUY: + case V4L2_PIX_FMT_KONICA420: + case V4L2_PIX_FMT_M420: + case V4L2_PIX_FMT_SN9C20X_I420: + case V4L2_PIX_FMT_CPIA1: + case V4L2_PIX_FMT_OV511: + case V4L2_PIX_FMT_OV518: { + unsigned char *d; + int d_size; + int yvu = 0; + + if (dest_pix_fmt != V4L2_PIX_FMT_YUV420 && + dest_pix_fmt != V4L2_PIX_FMT_YVU420) { + d = v4lconvert_alloc_buffer(width * height * 3 / 2, + &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); + if (!d) + return v4lconvert_oom_error(data); + d_size = width * height * 3 / 2; + } else { + d = dest; + d_size = dest_size; + } + + if (dest_pix_fmt == V4L2_PIX_FMT_YVU420) + yvu = 1; + + switch (src_pix_fmt) { + case V4L2_PIX_FMT_SPCA501: + v4lconvert_spca501_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_SPCA505: + v4lconvert_spca505_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_SPCA508: + v4lconvert_spca508_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_CIT_YYVYUY: + v4lconvert_cit_yyvyuy_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_KONICA420: + v4lconvert_konica_yuv420_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_M420: + v4lconvert_m420_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_SN9C20X_I420: + v4lconvert_sn9c20x_to_yuv420(src, d, width, height, yvu); + break; + case V4L2_PIX_FMT_CPIA1: + if (v4lconvert_cpia1_to_yuv420(data, src, src_size, d, + width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; + case V4L2_PIX_FMT_OV511: + if (v4lconvert_helper_decompress(data, LIBV4LCONVERT_PRIV_DIR "/ov511-decomp", + src, src_size, d, d_size, width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; + case V4L2_PIX_FMT_OV518: + if (v4lconvert_helper_decompress(data, LIBV4LCONVERT_PRIV_DIR "/ov518-decomp", + src, src_size, d, d_size, width, height, yvu)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + break; + } + + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yuv420_to_rgb24(data->convert_pixfmt_buf, dest, width, + height, yvu); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yuv420_to_bgr24(data->convert_pixfmt_buf, dest, width, + height, yvu); + break; + } + break; + } + + /* Conexant cx2341x raw video macroblock format */ + case V4L2_PIX_FMT_HM12: + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_hm12_to_rgb24(src, dest, width, height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_hm12_to_bgr24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_hm12_to_yuv420(src, dest, width, height, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_hm12_to_yuv420(src, dest, width, height, 1); + break; + } + break; + + /* compressed bayer formats */ + case V4L2_PIX_FMT_SPCA561: + case V4L2_PIX_FMT_SN9C10X: + case V4L2_PIX_FMT_PAC207: + case V4L2_PIX_FMT_MR97310A: +#ifdef HAVE_JPEG + case V4L2_PIX_FMT_JL2005BCD: +#endif + case V4L2_PIX_FMT_SN9C2028: + case V4L2_PIX_FMT_SQ905C: + case V4L2_PIX_FMT_STV0680: { /* Not compressed but needs some shuffling */ + unsigned char *tmpbuf; + struct v4l2_format tmpfmt = *fmt; + + tmpbuf = v4lconvert_alloc_buffer(width * height, + &data->convert_pixfmt_buf, &data->convert_pixfmt_buf_size); + if (!tmpbuf) + return v4lconvert_oom_error(data); + + switch (src_pix_fmt) { + case V4L2_PIX_FMT_SPCA561: + v4lconvert_decode_spca561(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG8; + break; + case V4L2_PIX_FMT_SN9C10X: + v4lconvert_decode_sn9c10x(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + break; + case V4L2_PIX_FMT_PAC207: + if (v4lconvert_decode_pac207(data, src, src_size, tmpbuf, + width, height)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + break; + case V4L2_PIX_FMT_MR97310A: + if (v4lconvert_decode_mr97310a(data, src, src_size, tmpbuf, + width, height)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + break; +#ifdef HAVE_JPEG + case V4L2_PIX_FMT_JL2005BCD: + if (v4lconvert_decode_jl2005bcd(data, src, src_size, + tmpbuf, + width, height)) { + /* Corrupt frame, better get another one */ + errno = EAGAIN; + return -1; + } + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; + break; +#endif + case V4L2_PIX_FMT_SN9C2028: + v4lconvert_decode_sn9c2028(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + break; + case V4L2_PIX_FMT_SQ905C: + v4lconvert_decode_sq905c(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; + break; + case V4L2_PIX_FMT_STV0680: + v4lconvert_decode_stv0680(src, tmpbuf, width, height); + tmpfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; + break; + } + /* Do processing on the tmp buffer, because doing it on bayer data is + cheaper, and bayer == rgb and our dest_fmt may be yuv */ + tmpfmt.fmt.pix.bytesperline = width; + tmpfmt.fmt.pix.sizeimage = width * height; + v4lprocessing_processing(data->processing, tmpbuf, &tmpfmt); + /* Deliberate fall through to raw bayer fmt code! */ + src_pix_fmt = tmpfmt.fmt.pix.pixelformat; + src = tmpbuf; + src_size = width * height; + /* fall through */ + } + + /* Raw bayer formats */ + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + if (src_size < (width * height)) { + V4LCONVERT_ERR("short raw bayer data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_bayer_to_rgb24(src, dest, width, height, bytesperline, src_pix_fmt); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_bayer_to_bgr24(src, dest, width, height, bytesperline, src_pix_fmt); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_bayer_to_yuv420(src, dest, width, height, bytesperline, src_pix_fmt, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_bayer_to_yuv420(src, dest, width, height, bytesperline, src_pix_fmt, 1); + break; + } + break; + + case V4L2_PIX_FMT_SE401: { + unsigned char *d = NULL; + + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + d = dest; + break; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + d = v4lconvert_alloc_buffer(width * height * 3, + &data->convert_pixfmt_buf, + &data->convert_pixfmt_buf_size); + if (!d) + return v4lconvert_oom_error(data); + + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; + v4lconvert_fixup_fmt(fmt); + break; + default: + V4LCONVERT_ERR("Unknown destination format in conversion\n"); + errno = EINVAL; + return -1; + } + + result = v4lconvert_se401_to_rgb24(data, src, src_size, d, + width, height); + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_BGR24: + v4lconvert_swap_rgb(d, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb24_to_yuv420(d, dest, fmt, 0, 0, 3); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(d, dest, fmt, 0, 1, 3); + break; + } + break; + } + + case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y16_BE: + if (src_size < (width * height * 2)) { + V4LCONVERT_ERR("short y16 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_y16_to_rgb24(src, dest, width, height, + src_pix_fmt == V4L2_PIX_FMT_Y16); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_y16_to_yuv420(src, dest, fmt, + src_pix_fmt == V4L2_PIX_FMT_Y16); + break; + } + break; + + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y4: + case V4L2_PIX_FMT_Y6: + if (src_size < (width * height)) { + V4LCONVERT_ERR("short grey data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + v4lconvert_grey_to_rgb24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + v4lconvert_grey_to_yuv420(src, dest, fmt); + break; + } + break; + + case V4L2_PIX_FMT_Y10BPACK: + if (src_size < (width * height * 10 / 8)) { + V4LCONVERT_ERR("short y10b data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + result = v4lconvert_y10b_to_rgb24(data, src, dest, + width, height); + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + result = v4lconvert_y10b_to_yuv420(data, src, dest, + width, height); + break; + } + break; + + case V4L2_PIX_FMT_RGB565: + if (src_size < (width * height * 2)) { + V4LCONVERT_ERR("short rgb565 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_rgb565_to_rgb24(src, dest, width, height); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_rgb565_to_bgr24(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb565_to_yuv420(src, dest, fmt, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb565_to_yuv420(src, dest, fmt, 1); + break; + } + break; + + case V4L2_PIX_FMT_RGB24: + if (src_size < (width * height * 3)) { + V4LCONVERT_ERR("short rgb24 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + memcpy(dest, src, width * height * 3); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_swap_rgb(src, dest, width, height); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0, 3); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1, 3); + break; + } + break; + + case V4L2_PIX_FMT_BGR24: + if (src_size < (width * height * 3)) { + V4LCONVERT_ERR("short bgr24 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_swap_rgb(src, dest, width, height); + break; + case V4L2_PIX_FMT_BGR24: + memcpy(dest, src, width * height * 3); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0, 3); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1, 3); + break; + } + break; + + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_XRGB32: + case V4L2_PIX_FMT_ARGB32: + if (src_size < (width * height * 4)) { + V4LCONVERT_ERR("short rgb32 data frame\n"); + errno = EPIPE; + result = -1; + } + src++; + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_rgb32_to_rgb24(src, dest, width, height, 0); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_rgb32_to_rgb24(src, dest, width, height, 1); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 0, 4); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 0, 1, 4); + break; + } + break; + + case V4L2_PIX_FMT_BGR32: + case V4L2_PIX_FMT_XBGR32: + case V4L2_PIX_FMT_ABGR32: + if (src_size < (width * height * 4)) { + V4LCONVERT_ERR("short bgr32 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_rgb32_to_rgb24(src, dest, width, height, 1); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_rgb32_to_rgb24(src, dest, width, height, 0); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 0, 4); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_rgb24_to_yuv420(src, dest, fmt, 1, 1, 4); + break; + } + break; + + case V4L2_PIX_FMT_YUV420: + if (src_size < (width * height * 3 / 2)) { + V4LCONVERT_ERR("short yuv420 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yuv420_to_rgb24(src, dest, width, + height, 0); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yuv420_to_bgr24(src, dest, width, + height, 0); + break; + case V4L2_PIX_FMT_YUV420: + memcpy(dest, src, width * height * 3 / 2); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_swap_uv(src, dest, fmt); + break; + } + break; + + case V4L2_PIX_FMT_YVU420: + if (src_size < (width * height * 3 / 2)) { + V4LCONVERT_ERR("short yvu420 data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yuv420_to_rgb24(src, dest, width, + height, 1); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yuv420_to_bgr24(src, dest, width, + height, 1); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_swap_uv(src, dest, fmt); + break; + case V4L2_PIX_FMT_YVU420: + memcpy(dest, src, width * height * 3 / 2); + break; + } + break; + + case V4L2_PIX_FMT_YUYV: + if (src_size < (width * height * 2)) { + V4LCONVERT_ERR("short yuyv data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yuyv_to_rgb24(src, dest, width, height, bytesperline); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yuyv_to_bgr24(src, dest, width, height, bytesperline); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_yuyv_to_yuv420(src, dest, width, height, bytesperline, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_yuyv_to_yuv420(src, dest, width, height, bytesperline, 1); + break; + } + break; + + case V4L2_PIX_FMT_YVYU: + if (src_size < (width * height * 2)) { + V4LCONVERT_ERR("short yvyu data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_yvyu_to_rgb24(src, dest, width, height, bytesperline); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_yvyu_to_bgr24(src, dest, width, height, bytesperline); + break; + case V4L2_PIX_FMT_YUV420: + /* Note we use yuyv_to_yuv420 not v4lconvert_yvyu_to_yuv420, + with the last argument reversed to make it have as we want */ + v4lconvert_yuyv_to_yuv420(src, dest, width, height, bytesperline, 1); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_yuyv_to_yuv420(src, dest, width, height, bytesperline, 0); + break; + } + break; + + case V4L2_PIX_FMT_UYVY: + if (src_size < (width * height * 2)) { + V4LCONVERT_ERR("short uyvy data frame\n"); + errno = EPIPE; + result = -1; + } + switch (dest_pix_fmt) { + case V4L2_PIX_FMT_RGB24: + v4lconvert_uyvy_to_rgb24(src, dest, width, height, bytesperline); + break; + case V4L2_PIX_FMT_BGR24: + v4lconvert_uyvy_to_bgr24(src, dest, width, height, bytesperline); + break; + case V4L2_PIX_FMT_YUV420: + v4lconvert_uyvy_to_yuv420(src, dest, width, height, bytesperline, 0); + break; + case V4L2_PIX_FMT_YVU420: + v4lconvert_uyvy_to_yuv420(src, dest, width, height, bytesperline, 1); + break; + } + break; + + default: + V4LCONVERT_ERR("Unknown src format in conversion\n"); + errno = EINVAL; + return -1; + } + + fmt->fmt.pix.pixelformat = dest_pix_fmt; + v4lconvert_fixup_fmt(fmt); + + return result; +} + +int v4lconvert_convert(struct v4lconvert_data *data, + const struct v4l2_format *src_fmt, /* in */ + const struct v4l2_format *dest_fmt, /* in */ + unsigned char *src, int src_size, unsigned char *dest, int dest_size) +{ + int res, dest_needed, temp_needed, processing, convert = 0; + int rotate90, vflip, hflip, crop; + unsigned char *convert1_dest = dest; + int convert1_dest_size = dest_size; + unsigned char *convert2_src = src, *convert2_dest = dest; + int convert2_dest_size = dest_size; + unsigned char *rotate90_src = src, *rotate90_dest = dest; + unsigned char *flip_src = src, *flip_dest = dest; + unsigned char *crop_src = src; + struct v4l2_format my_src_fmt = *src_fmt; + struct v4l2_format my_dest_fmt = *dest_fmt; + + processing = v4lprocessing_pre_processing(data->processing); + rotate90 = data->control_flags & V4LCONTROL_ROTATED_90_JPEG; + hflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_HFLIP); + vflip = v4lcontrol_get_ctrl(data->control, V4LCONTROL_VFLIP); + crop = my_dest_fmt.fmt.pix.width != my_src_fmt.fmt.pix.width || + my_dest_fmt.fmt.pix.height != my_src_fmt.fmt.pix.height; + + if (/* If no conversion/processing is needed */ + (src_fmt->fmt.pix.pixelformat == dest_fmt->fmt.pix.pixelformat && + !processing && !rotate90 && !hflip && !vflip && !crop) || + /* or if we should do processing/rotating/flipping but the app tries to + use the native cam format, we just return an unprocessed frame copy */ + !v4lconvert_supported_dst_format(dest_fmt->fmt.pix.pixelformat)) { + int to_copy = MIN(dest_size, src_size); + memcpy(dest, src, to_copy); + return to_copy; + } + + /* sanity check, is the dest buffer large enough? */ + switch (my_dest_fmt.fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + dest_needed = my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3; + temp_needed = my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; + break; + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + dest_needed = + my_dest_fmt.fmt.pix.width * my_dest_fmt.fmt.pix.height * 3 / 2; + temp_needed = + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3 / 2; + break; + default: + V4LCONVERT_ERR("Unknown dest format in conversion\n"); + errno = EINVAL; + return -1; + } + + if (dest_size < dest_needed) { + V4LCONVERT_ERR("destination buffer too small (%d < %d)\n", + dest_size, dest_needed); + errno = EFAULT; + return -1; + } + + + /* Sometimes we need foo -> rgb -> bar as video processing (whitebalance, + etc.) can only be done on rgb data */ + if (processing && v4lconvert_processing_needs_double_conversion( + my_src_fmt.fmt.pix.pixelformat, + my_dest_fmt.fmt.pix.pixelformat)) + convert = 2; + else if (my_dest_fmt.fmt.pix.pixelformat != + my_src_fmt.fmt.pix.pixelformat || + /* Special case if we do not need to do conversion, but we + are not doing any other step involving copying either, + force going through convert_pixfmt to copy the data from + source to dest */ + (!rotate90 && !hflip && !vflip && !crop)) + convert = 1; + + /* convert_pixfmt (only if convert == 2) -> processing -> convert_pixfmt -> + rotate -> flip -> crop, all steps are optional */ + if (convert == 2) { + convert1_dest = v4lconvert_alloc_buffer( + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3, + &data->convert1_buf, &data->convert1_buf_size); + if (!convert1_dest) + return v4lconvert_oom_error(data); + + convert1_dest_size = + my_src_fmt.fmt.pix.width * my_src_fmt.fmt.pix.height * 3; + convert2_src = convert1_dest; + } + + if (convert && (rotate90 || hflip || vflip || crop)) { + convert2_dest = v4lconvert_alloc_buffer(temp_needed, + &data->convert2_buf, &data->convert2_buf_size); + if (!convert2_dest) + return v4lconvert_oom_error(data); + + convert2_dest_size = temp_needed; + rotate90_src = flip_src = crop_src = convert2_dest; + } + + if (rotate90 && (hflip || vflip || crop)) { + rotate90_dest = v4lconvert_alloc_buffer(temp_needed, + &data->rotate90_buf, &data->rotate90_buf_size); + if (!rotate90_dest) + return v4lconvert_oom_error(data); + + flip_src = crop_src = rotate90_dest; + } + + if ((vflip || hflip) && crop) { + flip_dest = v4lconvert_alloc_buffer(temp_needed, &data->flip_buf, + &data->flip_buf_size); + if (!flip_dest) + return v4lconvert_oom_error(data); + + crop_src = flip_dest; + } + + /* Done setting sources / dest and allocating intermediate buffers, + real conversion / processing / ... starts here. */ + if (convert == 2) { + res = v4lconvert_convert_pixfmt(data, src, src_size, + convert1_dest, convert1_dest_size, + &my_src_fmt, + V4L2_PIX_FMT_RGB24); + if (res) + return res; + + src_size = my_src_fmt.fmt.pix.sizeimage; + } + + if (processing) + v4lprocessing_processing(data->processing, convert2_src, &my_src_fmt); + + if (convert) { + res = v4lconvert_convert_pixfmt(data, convert2_src, src_size, + convert2_dest, convert2_dest_size, + &my_src_fmt, + my_dest_fmt.fmt.pix.pixelformat); + if (res) + return res; + + src_size = my_src_fmt.fmt.pix.sizeimage; + + /* We call processing here again in case the source format was not + rgb, but the dest is. v4lprocessing checks it self it only actually + does the processing once per frame. */ + if (processing) + v4lprocessing_processing(data->processing, convert2_dest, &my_src_fmt); + } + + if (rotate90) + v4lconvert_rotate90(rotate90_src, rotate90_dest, &my_src_fmt); + + if (hflip || vflip) + v4lconvert_flip(flip_src, flip_dest, &my_src_fmt, hflip, vflip); + + if (crop) + v4lconvert_crop(crop_src, dest, &my_src_fmt, &my_dest_fmt); + + return dest_needed; +} + +const char *v4lconvert_get_error_message(struct v4lconvert_data *data) +{ + return data->error_msg; +} + +static void v4lconvert_get_framesizes(struct v4lconvert_data *data, + unsigned int pixelformat, int index) +{ + unsigned int i, j, match; + struct v4l2_frmsizeenum frmsize = { .pixel_format = pixelformat }; + + for (i = 0; ; i++) { + frmsize.index = i; + if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUM_FRAMESIZES, &frmsize)) + break; + + /* We got a framesize, check we don't have the same one already */ + match = 0; + for (j = 0; j < data->no_framesizes; j++) { + if (frmsize.type != data->framesizes[j].type) + continue; + + switch (frmsize.type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + if (!memcmp(&frmsize.discrete, &data->framesizes[j].discrete, + sizeof(frmsize.discrete))) + match = 1; + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: + if (!memcmp(&frmsize.stepwise, &data->framesizes[j].stepwise, + sizeof(frmsize.stepwise))) + match = 1; + break; + } + if (match) + break; + } + /* Add this framesize if it is not already in our list */ + if (!match) { + if (data->no_framesizes == V4LCONVERT_MAX_FRAMESIZES) { + fprintf(stderr, "libv4lconvert: warning more framesizes then I can handle!\n"); + return; + } + data->framesizes[data->no_framesizes].type = frmsize.type; + /* We use the pixel_format member to store a bitmask of all + supported src_formats which can do this size */ + data->framesizes[data->no_framesizes].pixel_format = 1 << index; + + switch (frmsize.type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + data->framesizes[data->no_framesizes].discrete = frmsize.discrete; + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: + data->framesizes[data->no_framesizes].stepwise = frmsize.stepwise; + break; + } + data->no_framesizes++; + } else { + data->framesizes[j].pixel_format |= 1 << index; + } + } +} + +int v4lconvert_enum_framesizes(struct v4lconvert_data *data, + struct v4l2_frmsizeenum *frmsize) +{ + if (!v4lconvert_supported_dst_format(frmsize->pixel_format)) { + if (v4lconvert_supported_dst_fmt_only(data)) { + errno = EINVAL; + return -1; + } + return data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUM_FRAMESIZES, frmsize); + } + + if (frmsize->index >= data->no_framesizes) { + errno = EINVAL; + return -1; + } + + frmsize->type = data->framesizes[frmsize->index].type; + memset(frmsize->reserved, 0, sizeof(frmsize->reserved)); + switch (frmsize->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + frmsize->discrete = data->framesizes[frmsize->index].discrete; + /* Apply the same rounding algorithm as v4lconvert_try_format */ + frmsize->discrete.width &= ~7; + frmsize->discrete.height &= ~1; + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: + frmsize->stepwise = data->framesizes[frmsize->index].stepwise; + break; + } + + return 0; +} + +int v4lconvert_enum_frameintervals(struct v4lconvert_data *data, + struct v4l2_frmivalenum *frmival) +{ + int res; + struct v4l2_format src_fmt, dest_fmt; + + if (!v4lconvert_supported_dst_format(frmival->pixel_format)) { + if (v4lconvert_supported_dst_fmt_only(data)) { + errno = EINVAL; + return -1; + } + res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUM_FRAMEINTERVALS, frmival); + if (res) + V4LCONVERT_ERR("%s\n", strerror(errno)); + return res; + } + + /* Check which format we will be using to convert to frmival->pixel_format */ + memset(&dest_fmt, 0, sizeof(dest_fmt)); + dest_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dest_fmt.fmt.pix.pixelformat = frmival->pixel_format; + dest_fmt.fmt.pix.width = frmival->width; + dest_fmt.fmt.pix.height = frmival->height; + res = v4lconvert_try_format(data, &dest_fmt, &src_fmt); + if (res) { + V4LCONVERT_ERR("trying format: %s\n", strerror(errno)); + return res; + } + + /* Check the requested format is supported exactly as requested */ + if (dest_fmt.fmt.pix.pixelformat != frmival->pixel_format || + dest_fmt.fmt.pix.width != frmival->width || + dest_fmt.fmt.pix.height != frmival->height) { + int frmival_pixformat = frmival->pixel_format; + int dest_pixformat = dest_fmt.fmt.pix.pixelformat; + + V4LCONVERT_ERR("Could not find matching framesize for: %c%c%c%c %dx%d " + "closest match: %c%c%c%c %dx%d\n", + frmival_pixformat & 0xff, + (frmival_pixformat >> 8) & 0xff, + (frmival_pixformat >> 16) & 0xff, + frmival_pixformat >> 24, + frmival->width, frmival->height, + dest_pixformat & 0xff, + (dest_pixformat >> 8) & 0xff, + (dest_pixformat >> 16) & 0xff, + dest_pixformat >> 24, + dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height); + errno = EINVAL; + return -1; + } + + /* Enumerate the frameintervals of the source format we will be using */ + frmival->pixel_format = src_fmt.fmt.pix.pixelformat; + frmival->width = src_fmt.fmt.pix.width; + frmival->height = src_fmt.fmt.pix.height; + res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd, + VIDIOC_ENUM_FRAMEINTERVALS, frmival); + if (res) { + int dest_pixfmt = dest_fmt.fmt.pix.pixelformat; + int src_pixfmt = src_fmt.fmt.pix.pixelformat; + + V4LCONVERT_ERR("Could not enum frameival index: %d for: %c%c%c%c %dx%d " + "using src: %c%c%c%c %dx%d, error: %s\n", + frmival->index, + dest_pixfmt & 0xff, + (dest_pixfmt >> 8) & 0xff, + (dest_pixfmt >> 16) & 0xff, + dest_pixfmt >> 24, + dest_fmt.fmt.pix.width , dest_fmt.fmt.pix.height, + src_pixfmt & 0xff, + (src_pixfmt >> 8) & 0xff, + (src_pixfmt >> 16) & 0xff, + src_pixfmt >> 24, + src_fmt.fmt.pix.width, src_fmt.fmt.pix.height, strerror(errno)); + } + + /* Restore the requested format in the frmival struct */ + frmival->pixel_format = dest_fmt.fmt.pix.pixelformat; + frmival->width = dest_fmt.fmt.pix.width; + frmival->height = dest_fmt.fmt.pix.height; + + return res; +} + +int v4lconvert_vidioc_queryctrl(struct v4lconvert_data *data, void *arg) +{ + return v4lcontrol_vidioc_queryctrl(data->control, arg); +} + +int v4lconvert_vidioc_g_ctrl(struct v4lconvert_data *data, void *arg) +{ + return v4lcontrol_vidioc_g_ctrl(data->control, arg); +} + +int v4lconvert_vidioc_s_ctrl(struct v4lconvert_data *data, void *arg) +{ + return v4lcontrol_vidioc_s_ctrl(data->control, arg); +} + +int v4lconvert_vidioc_g_ext_ctrls(struct v4lconvert_data *data, void *arg) +{ + return v4lcontrol_vidioc_g_ext_ctrls(data->control, arg); +} + +int v4lconvert_vidioc_try_ext_ctrls(struct v4lconvert_data *data, void *arg) +{ + return v4lcontrol_vidioc_try_ext_ctrls(data->control, arg); +} + +int v4lconvert_vidioc_s_ext_ctrls(struct v4lconvert_data *data, void *arg) +{ + return v4lcontrol_vidioc_s_ext_ctrls(data->control, arg); +} + +int v4lconvert_get_fps(struct v4lconvert_data *data) +{ + return data->fps; +} + +void v4lconvert_set_fps(struct v4lconvert_data *data, int fps) +{ + data->fps = fps; +} diff --git a/libv4lconvert/libv4lconvert.export b/libv4lconvert/libv4lconvert.export new file mode 100644 index 0000000..2635d99 --- /dev/null +++ b/libv4lconvert/libv4lconvert.export @@ -0,0 +1,23 @@ +# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +v4lconvert_get_default_dev_ops +v4lconvert_create +v4lconvert_create_with_dev_ops +v4lconvert_destroy +v4lconvert_supported_dst_fmt_only +v4lconvert_try_format +v4lconvert_enum_fmt +v4lconvert_needs_conversion +v4lconvert_convert +v4lconvert_get_error_message +v4lconvert_enum_framesizes +v4lconvert_enum_frameintervals +v4lconvert_vidioc_queryctrl +v4lconvert_vidioc_g_ctrl +v4lconvert_vidioc_s_ctrl +v4lconvert_vidioc_g_ext_ctrls +v4lconvert_vidioc_try_ext_ctrls +v4lconvert_vidioc_s_ext_ctrls +v4lconvert_supported_dst_format +v4lconvert_get_fps +v4lconvert_set_fps +v4lconvert_fixup_fmt diff --git a/libv4lconvert/libv4lconvert.pc.in b/libv4lconvert/libv4lconvert.pc.in new file mode 100644 index 0000000..2b65d8b --- /dev/null +++ b/libv4lconvert/libv4lconvert.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +includedir=@includedir@ +libdir=@libdir@ + +Name: libv4lconvert +Description: v4l format conversion library +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lv4lconvert +Libs.private: -lrt -lm @JPEG_LIBS@ +Cflags: -I${includedir} diff --git a/libv4lconvert/libv4lsyscall-priv.h b/libv4lconvert/libv4lsyscall-priv.h new file mode 100644 index 0000000..bc18b21 --- /dev/null +++ b/libv4lconvert/libv4lsyscall-priv.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2009 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The following file allows for having the complete V4L stack and + * hardware drivers in userland. + */ + +#ifndef _LIBV4LSYSCALL_PRIV_H_ +#define _LIBV4LSYSCALL_PRIV_H_ + +/* Some of these headers are not needed by us, but by linux/videodev2.h, + which is broken on some systems and doesn't include them itself :( */ + +#ifdef linux +#include +#include +#include +#include +/* On 32 bits archs we always use mmap2, on 64 bits archs there is no mmap2 */ +#ifdef __NR_mmap2 +#if !defined(SYS_mmap2) +#define SYS_mmap2 __NR_mmap2 +#endif +#define MMAP2_PAGE_SHIFT 12 +#else +#define SYS_mmap2 SYS_mmap +#define MMAP2_PAGE_SHIFT 0 +#endif +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#include +#include +#include +#define _IOC_NR(cmd) ((cmd) & 0xFF) +#define _IOC_TYPE(cmd) IOCGROUP(cmd) +#define _IOC_SIZE(cmd) IOCPARM_LEN(cmd) +#define MAP_ANONYMOUS MAP_ANON +#define MMAP2_PAGE_SHIFT 0 +#endif + +#undef SYS_OPEN +#undef SYS_CLOSE +#undef SYS_IOCTL +#undef SYS_READ +#undef SYS_WRITE +#undef SYS_MMAP +#undef SYS_MUNMAP + +#ifndef CONFIG_SYS_WRAPPER + +#ifdef SYS_openat +#define SYS_OPEN(file, oflag, mode) \ + syscall(SYS_openat, AT_FDCWD, (const char *)(file), (int)(oflag), (mode_t)(mode)) +#else +#define SYS_OPEN(file, oflag, mode) \ + syscall(SYS_open, (const char *)(file), (int)(oflag), (mode_t)(mode)) +#endif +#define SYS_CLOSE(fd) \ + syscall(SYS_close, (int)(fd)) +#define SYS_IOCTL(fd, cmd, arg) \ + syscall(SYS_ioctl, (int)(fd), (unsigned long)(cmd), (void *)(arg)) +#define SYS_READ(fd, buf, len) \ + syscall(SYS_read, (int)(fd), (void *)(buf), (size_t)(len)); +#define SYS_WRITE(fd, buf, len) \ + syscall(SYS_write, (int)(fd), (const void *)(buf), (size_t)(len)); + +#if defined(__FreeBSD__) +#define SYS_MMAP(addr, len, prot, flags, fd, off) \ + __syscall(SYS_mmap, (void *)(addr), (size_t)(len), \ + (int)(prot), (int)(flags), (int)(fd), (off_t)(off)) +#elif defined(__FreeBSD_kernel__) +#define SYS_MMAP(addr, len, prot, flags, fd, off) \ + syscall(SYS_mmap, (void *)(addr), (size_t)(len), \ + (int)(prot), (int)(flags), (int)(fd), (off_t)(off)) +#else +#define SYS_MMAP(addr, len, prot, flags, fd, off) \ + syscall(SYS_mmap2, (void *)(addr), (size_t)(len), \ + (int)(prot), (int)(flags), (int)(fd), (off_t)((off) >> MMAP2_PAGE_SHIFT)) +#endif + +#define SYS_MUNMAP(addr, len) \ + syscall(SYS_munmap, (void *)(addr), (size_t)(len)) + +#else + +int v4lx_open_wrapper(const char *, int, int); +int v4lx_close_wrapper(int); +int v4lx_ioctl_wrapper(int, unsigned long, void *); +int v4lx_read_wrapper(int, void *, size_t); +int v4lx_write_wrapper(int, const void *, size_t); +void *v4lx_mmap_wrapper(void *, size_t, int, int, int, off_t); +int v4lx_munmap_wrapper(void *, size_t); + +#define SYS_OPEN(...) v4lx_open_wrapper(__VA_ARGS__) +#define SYS_CLOSE(...) v4lx_close_wrapper(__VA_ARGS__) +#define SYS_IOCTL(...) v4lx_ioctl_wrapper(__VA_ARGS__) +#define SYS_READ(...) v4lx_read_wrapper(__VA_ARGS__) +#define SYS_WRITE(...) v4lx_write_wrapper(__VA_ARGS__) +#define SYS_MMAP(...) v4lx_mmap_wrapper(__VA_ARGS__) +#define SYS_MUNMAP(...) v4lx_munmap_wrapper(__VA_ARGS__) + +#endif + +#endif /* _LIBV4LSYSCALL_PRIV_H_ */ diff --git a/libv4lconvert/mr97310a.c b/libv4lconvert/mr97310a.c new file mode 100644 index 0000000..6a8588d --- /dev/null +++ b/libv4lconvert/mr97310a.c @@ -0,0 +1,208 @@ +/* + * MR97310A decoder + * + * Copyright (C) 2004-2009 Theodore Kilgore + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "libv4lconvert-priv.h" +#include "libv4lsyscall-priv.h" + +#define CLIP(x) ((x) < 0 ? 0 : ((x) > 0xff) ? 0xff : (x)) + +#define MIN_CLOCKDIV_CID V4L2_CID_PRIVATE_BASE + +/* FIXME not threadsafe */ +static int decoder_initialized; + +static struct { + unsigned char is_abs; + unsigned char len; + signed char val; +} table[256]; + +static void init_mr97310a_decoder(void) +{ + int i; + int is_abs, val, len; + + for (i = 0; i < 256; ++i) { + is_abs = 0; + val = 0; + len = 0; + if ((i & 0x80) == 0) { + /* code 0 */ + val = 0; + len = 1; + } else if ((i & 0xe0) == 0xc0) { + /* code 110 */ + val = -3; + len = 3; + } else if ((i & 0xe0) == 0xa0) { + /* code 101 */ + val = 3; + len = 3; + } else if ((i & 0xf0) == 0x80) { + /* code 1000 */ + val = 8; + len = 4; + } else if ((i & 0xf0) == 0x90) { + /* code 1001 */ + val = -8; + len = 4; + } else if ((i & 0xf0) == 0xf0) { + /* code 1111 */ + val = -20; + len = 4; + } else if ((i & 0xf8) == 0xe0) { + /* code 11100 */ + val = 20; + len = 5; + } else if ((i & 0xf8) == 0xe8) { + /* code 11101xxxxx */ + is_abs = 1; + val = 0; /* value is calculated later */ + len = 5; + } + table[i].is_abs = is_abs; + table[i].val = val; + table[i].len = len; + } + decoder_initialized = 1; +} + +static inline unsigned char get_byte(const unsigned char *inp, + unsigned int bitpos) +{ + const unsigned char *addr; + + addr = inp + (bitpos >> 3); + return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); +} + +int v4lconvert_decode_mr97310a(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, + unsigned char *outp, int width, int height) +{ + int row, col; + int val; + int bitpos; + unsigned char code; + unsigned char lp, tp, tlp, trp; + struct v4l2_control min_clockdiv = { .id = MIN_CLOCKDIV_CID }; + + if (!decoder_initialized) + init_mr97310a_decoder(); + + /* remove the header */ + inp += 12; + + bitpos = 0; + + /* main decoding loop */ + for (row = 0; row < height; ++row) { + col = 0; + + /* first two pixels in first two rows are stored as raw 8-bit */ + if (row < 2) { + code = get_byte(inp, bitpos); + bitpos += 8; + *outp++ = code; + + code = get_byte(inp, bitpos); + bitpos += 8; + *outp++ = code; + + col += 2; + } + + while (col < width) { + /* get bitcode */ + code = get_byte(inp, bitpos); + /* update bit position */ + bitpos += table[code].len; + + /* calculate pixel value */ + if (table[code].is_abs) { + /* get 5 more bits and use them as absolute value */ + code = get_byte(inp, bitpos); + val = (code & 0xf8); + bitpos += 5; + + } else { + /* value is relative to top or left pixel */ + val = table[code].val; + lp = outp[-2]; + if (row > 1) { + tlp = outp[-2 * width - 2]; + tp = outp[-2 * width]; + trp = outp[-2 * width + 2]; + } + if (row < 2) { + /* top row: relative to left pixel */ + val += lp; + } else if (col < 2) { + /* left column: relative to top pixel */ + /* initial estimate */ + val += (tp + trp) / 2; + } else if (col > width - 3) { + /* left column: relative to top pixel */ + val += (tp + lp + tlp + 1) / 3; + /* main area: weighted average of tlp, trp, + * lp, and tp */ + } else { + tlp >>= 1; + trp >>= 1; + /* initial estimate for predictor */ + val += (lp + tp + tlp + trp + 1) / 3; + } + } + /* store pixel */ + *outp++ = CLIP(val); + ++col; + } + + /* src_size - 12 because of 12 byte footer */ + if (((bitpos - 1) / 8) >= (src_size - 12)) { + data->frames_dropped++; + if (data->frames_dropped == 3) { + /* Tell the driver to go slower as + the compression engine is not able to + compress the image enough, we may + fail to do this because older + drivers don't support this */ + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, + &min_clockdiv); + min_clockdiv.value++; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, + &min_clockdiv); + /* We return success here, because if we + return failure for too many frames in a row + libv4l2 will return an error to the + application and some applications abort + on the first error received. */ + data->frames_dropped = 0; + return 0; + } + V4LCONVERT_ERR("incomplete mr97310a frame\n"); + return -1; + } + } + + data->frames_dropped = 0; + return 0; +} diff --git a/libv4lconvert/ov511-decomp.c b/libv4lconvert/ov511-decomp.c new file mode 100644 index 0000000..971d497 --- /dev/null +++ b/libv4lconvert/ov511-decomp.c @@ -0,0 +1,666 @@ +/* We would like to embed this inside libv4l, but we cannot as I've failed + to contact Mark W. McClelland to get permission to relicense this, + so this lives in an external (GPL licensed) helper */ + +/* OV511 Decompression Support Module + * + * Copyright (c) 1999-2003 Mark W. McClelland. All rights reserved. + * http://alpha.dyndns.org/ov511/ + * + * Original decompression code Copyright 1998-2000 OmniVision Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include "helper-funcs.h" + +/****************************************************************************** + * Decompression Functions + ******************************************************************************/ + +static void DecompressYHI(unsigned char *pIn, + unsigned char *pOut, + int *iIn, /* in/out */ + int *iOut, /* in/out */ + const int w, + const int YUVFlag) +{ + short ZigZag[64]; + int temp[64]; + int Zcnt_Flag = 0; + int Num8_Flag = 0; + int in_pos = *iIn; + int out_pos = *iOut; + int tmp, tmp1, tmp2, tmp3; + unsigned char header, ZTable[64]; + short tmpl, tmph, half_byte, idx, count; + unsigned long ZigZag_length = 0, ZT_length, i, j; + short DeZigZag[64]; + + const short a = 11584; + const short b = 16068; + const short c = 15136; + const short d = 13624; + const short e = 9104; + const short f = 6270; + const short g = 3196; + + int out_idx; + + /* Take off every 'Zig' */ + for (i = 0; i < 64; i++) + ZigZag[i] = 0; + + /***************************** + * Read in the Y header byte * + *****************************/ + + header = pIn[in_pos]; + in_pos++; + + ZigZag_length = header & 0x3f; + ZigZag_length = ZigZag_length + 1; + + Num8_Flag = header & 0x40; + Zcnt_Flag = header & 0x80; + + /************************* + * Read in the Y content * + *************************/ + + if (Zcnt_Flag == 0) { /* Without Zero Table read contents directly */ + /* Read in ZigZag[0] */ + ZigZag[0] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl << 8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0] << 4; + ZigZag[0] = ZigZag[0] >> 4; + + if (Num8_Flag) { /* 8 Bits */ + for (i = 1; i < ZigZag_length; i++) { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i] << 8; + ZigZag[i] = ZigZag[i] >> 8; + } + } else { /* 12 bits and has no Zero Table */ + idx = 1; + half_byte = 0; + for (i = 1; i < ZigZag_length; i++) { + if (half_byte == 0) { + ZigZag[i] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl << 8; + tmph = tmph & 0x0f00; + ZigZag[i] = ZigZag[i] | tmph; + ZigZag[i] = ZigZag[i] << 4; + ZigZag[i] = ZigZag[i] >> 4; + half_byte = 1; + } else { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i] << 8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i] >> 4; + half_byte = 0; + } + } + } + } else { /* Has Zero Table */ + /* Calculate Z-Table length */ + ZT_length = ZigZag_length / 8; + tmp = ZigZag_length % 8; + + if (tmp > 0) + ZT_length = ZT_length + 1; + + /* Read in Zero Table */ + for (j = 0; j < ZT_length; j++) + ZTable[j] = pIn[in_pos++]; + + /* Read in ZigZag[0] */ + ZigZag[0] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl << 8; + ZigZag[0] = ZigZag[0] | tmph; + ZigZag[0] = ZigZag[0] << 4; + ZigZag[0] = ZigZag[0] >> 4; + + /* Decode ZigZag */ + idx = 0; + ZTable[idx] = ZTable[idx] << 1; + count = 7; + + if (Num8_Flag) { /* 8 Bits and has zero table */ + for (i = 1; i < ZigZag_length; i++) { + if ((ZTable[idx] & 0x80)) { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i] << 8; + ZigZag[i] = ZigZag[i] >> 8; + } + + ZTable[idx] = ZTable[idx]<<1; + count--; + if (count == 0) { + count = 8; + idx++; + } + } + } else { /* 12 bits and has Zero Table */ + half_byte = 0; + for (i = 1; i < ZigZag_length; i++) { + if (ZTable[idx] & 0x80) { + if (half_byte == 0) { + ZigZag[i] = pIn[in_pos++]; + tmpl = pIn[in_pos++]; + tmph = tmpl << 8; + tmph = tmph & 0x0f00; + ZigZag[i] = ZigZag[i] | tmph; + ZigZag[i] = ZigZag[i] << 4; + ZigZag[i] = ZigZag[i] >> 4; + half_byte = 1; + } else { + ZigZag[i] = pIn[in_pos++]; + ZigZag[i] = ZigZag[i] << 8; + tmpl = tmpl & 0x00f0; + ZigZag[i] = ZigZag[i] | tmpl; + ZigZag[i] = ZigZag[i] >> 4; + half_byte = 0; + } + } + + ZTable[idx] = ZTable[idx] << 1; + count--; + if (count == 0) { + count = 8; + idx++; + } + } + } + } + + /************* + * De-ZigZag * + *************/ + + for (j = 0; j < 64; j++) + DeZigZag[j] = 0; + + if (YUVFlag == 1) { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1] << 1; + DeZigZag[2] = ZigZag[5] << 1; + DeZigZag[3] = ZigZag[6] << 2; + + DeZigZag[8] = ZigZag[2] << 1; + DeZigZag[9] = ZigZag[4] << 1; + DeZigZag[10] = ZigZag[7] << 1; + DeZigZag[11] = ZigZag[13] << 2; + + DeZigZag[16] = ZigZag[3] << 1; + DeZigZag[17] = ZigZag[8] << 1; + DeZigZag[18] = ZigZag[12] << 2; + DeZigZag[19] = ZigZag[17] << 2; + + DeZigZag[24] = ZigZag[9] << 2; + DeZigZag[25] = ZigZag[11] << 2; + DeZigZag[26] = ZigZag[18] << 2; + DeZigZag[27] = ZigZag[24] << 3; + } else { + DeZigZag[0] = ZigZag[0]; + DeZigZag[1] = ZigZag[1] << 2; + DeZigZag[2] = ZigZag[5] << 2; + DeZigZag[3] = ZigZag[6] << 3; + + DeZigZag[8] = ZigZag[2] << 2; + DeZigZag[9] = ZigZag[4] << 2; + DeZigZag[10] = ZigZag[7] << 2; + DeZigZag[11] = ZigZag[13] << 4; + + DeZigZag[16] = ZigZag[3] << 2; + DeZigZag[17] = ZigZag[8] << 2; + DeZigZag[18] = ZigZag[12] << 3; + DeZigZag[19] = ZigZag[17] << 4; + + DeZigZag[24] = ZigZag[9] << 3; + DeZigZag[25] = ZigZag[11] << 4; + DeZigZag[26] = ZigZag[18] << 4; + DeZigZag[27] = ZigZag[24] << 4; + } + + /***************** + **** IDCT 1D **** + *****************/ + +#define IDCT_1D(c0, c1, c2, c3, in) \ + do { \ + tmp1 = ((c0) * DeZigZag[in]) + ((c2) * DeZigZag[(in) + 2]); \ + tmp2 = (c1) * DeZigZag[(in) + 1]; \ + tmp3 = (c3) * DeZigZag[(in) + 3]; \ + } while (0) + +#define COMPOSE_1(out1, out2) \ + do { \ + tmp = tmp1 + tmp2 + tmp3; \ + temp[out1] = tmp >> 15; \ + tmp = tmp1 - tmp2 - tmp3; \ + temp[out2] = tmp >> 15; \ + } while (0) + +#define COMPOSE_2(out1, out2) \ + do { \ + tmp = tmp1 + tmp2 - tmp3; \ + temp[out1] = tmp >> 15; \ + tmp = tmp1 - tmp2 + tmp3; \ + temp[out2] = tmp >> 15; \ + } while (0) + + /* j = 0 */ + IDCT_1D(a, b, c, d, 0); COMPOSE_1(0, 56); + IDCT_1D(a, b, c, d, 8); COMPOSE_1(1, 57); + IDCT_1D(a, b, c, d, 16); COMPOSE_1(2, 58); + IDCT_1D(a, b, c, d, 24); COMPOSE_1(3, 59); + + /* j = 1 */ + IDCT_1D(a, d, f, g, 0); COMPOSE_2(8, 48); + IDCT_1D(a, d, f, g, 8); COMPOSE_2(9, 49); + IDCT_1D(a, d, f, g, 16); COMPOSE_2(10, 50); + IDCT_1D(a, d, f, g, 24); COMPOSE_2(11, 51); + + /* j = 2 */ + IDCT_1D(a, e, -f, b, 0); COMPOSE_2(16, 40); + IDCT_1D(a, e, -f, b, 8); COMPOSE_2(17, 41); + IDCT_1D(a, e, -f, b, 16); COMPOSE_2(18, 42); + IDCT_1D(a, e, -f, b, 24); COMPOSE_2(19, 43); + + /* j = 3 */ + IDCT_1D(a, g, -c, e, 0); COMPOSE_2(24, 32); + IDCT_1D(a, g, -c, e, 8); COMPOSE_2(25, 33); + IDCT_1D(a, g, -c, e, 16); COMPOSE_2(26, 34); + IDCT_1D(a, g, -c, e, 24); COMPOSE_2(27, 35); + +#undef IDCT_1D +#undef COMPOSE_1 +#undef COMPOSE_2 + + /***************** + **** IDCT 2D **** + *****************/ + +#define IDCT_2D(c0, c1, c2, c3, in) \ + do { \ + tmp = temp[in] * (c0) + temp[(in) + 1] * (c1) \ + + temp[(in) + 2] * (c2) + temp[(in) + 3] * (c3); \ + } while (0) + +#define STORE(i) \ + do { \ + tmp = tmp >> 15; \ + tmp = tmp + 128; \ + if (tmp > 255) \ + tmp = 255; \ + if (tmp < 0) \ + tmp = 0; \ + pOut[i] = (unsigned char)tmp; \ + } while (0) + +#define IDCT_2D_ROW(in) \ + do { \ + IDCT_2D(a, b, c, d, in); STORE(0 + out_idx); \ + IDCT_2D(a, d, f, -g, in); STORE(1 + out_idx); \ + IDCT_2D(a, e, -f, -b, in); STORE(2 + out_idx); \ + IDCT_2D(a, g, -c, -e, in); STORE(3 + out_idx); \ + IDCT_2D(a, -g, -c, e, in); STORE(4 + out_idx); \ + IDCT_2D(a, -e, -f, b, in); STORE(5 + out_idx); \ + IDCT_2D(a, -d, f, g, in); STORE(6 + out_idx); \ + IDCT_2D(a, -b, c, -d, in); STORE(7 + out_idx); \ + } while (0) + + +#define IDCT_2D_FAST(c0, c1, c2, c3, in) \ + do { \ + tmp1 = ((c0) * temp[in]) + ((c2) * temp[(in) + 2]); \ + tmp2 = (c1) * temp[(in) + 1]; \ + tmp3 = (c3) * temp[(in) + 3]; \ + } while (0) + +#define STORE_FAST_1(out1, out2) \ + do { \ + tmp = tmp1 + tmp2 + tmp3; \ + STORE((out1) + out_idx); \ + tmp = tmp1 - tmp2 - tmp3; \ + STORE((out2) + out_idx); \ + } while (0) + +#define STORE_FAST_2(out1, out2) \ + do { \ + tmp = tmp1 + tmp2 - tmp3; \ + STORE((out1) + out_idx); \ + tmp = tmp1 - tmp2 + tmp3; \ + STORE((out2) + out_idx); \ + } while (0) + +#define IDCT_2D_FAST_ROW(in) \ + do { \ + IDCT_2D_FAST(a, b, c, d, in); STORE_FAST_1(0, 7); \ + IDCT_2D_FAST(a, d, f, g, in); STORE_FAST_2(1, 6); \ + IDCT_2D_FAST(a, e, -f, b, in); STORE_FAST_2(2, 5); \ + IDCT_2D_FAST(a, g, -c, e, in); STORE_FAST_2(3, 4); \ + } while (0) + + out_idx = out_pos; + + IDCT_2D_ROW(0); out_idx += w; + IDCT_2D_ROW(8); out_idx += w; + IDCT_2D_ROW(16); out_idx += w; + IDCT_2D_ROW(24); out_idx += w; + IDCT_2D_ROW(32); out_idx += w; + IDCT_2D_ROW(40); out_idx += w; + IDCT_2D_FAST_ROW(48); out_idx += w; + IDCT_2D_FAST_ROW(56); + + *iIn = in_pos; + *iOut = out_pos + 8; +} + +#define DECOMP_Y() DecompressYHI(pIn, pY, &iIn, &iY, w, 1) +#define DECOMP_U() DecompressYHI(pIn, pU, &iIn, &iU, w / 2, 2) +#define DECOMP_V() DecompressYHI(pIn, pV, &iIn, &iV, w / 2, 2) + +#if 0 +static inline int +Decompress400HiNoMMX(unsigned char *pIn, + unsigned char *pOut, + const int w, + const int h, + const int inSize) +{ + unsigned char *pY = pOut; + int x, y, iIn, iY; + + iIn = 0; + for (y = 0; y < h; y += 8) { + iY = w * y; + + for (x = 0; x < w; x += 8) + DECOMP_Y(); + } + + return 0; +} +#endif + +static inline int +Decompress420HiNoMMX(unsigned char *pIn, + unsigned char *pOut, + const int w, + const int h, + const int inSize) +{ + unsigned char *pY = pOut; + unsigned char *pU = pY + w * h; + unsigned char *pV = pU + w * h / 4; + int xY, xUV, iY, iU, iV, iIn, count; + const int nBlocks = (w * h) / (32 * 8); + + iIn = 0; + iY = iU = iV = 0; + xY = xUV = 0; + + for (count = 0; count < nBlocks; count++) { + DECOMP_U(); + DECOMP_V(); xUV += 16; + if (xUV >= w) { + iU += (w * 7) / 2; + iV += (w * 7) / 2; + xUV = 0; + } + + DECOMP_Y(); xY += 8; + DECOMP_Y(); xY += 8; + if (xY >= w) { + iY += w * 7; + xY = 0; + } + DECOMP_Y(); xY += 8; + DECOMP_Y(); xY += 8; + if (xY >= w) { + iY += w * 7; + xY = 0; + } + } + + return 0; +} + +/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the + * image at pOut is specified by w. + */ + static inline void +make_8x8(unsigned char *pIn, unsigned char *pOut, int w) +{ + unsigned char *pOut1 = pOut; + int x, y; + + for (y = 0; y < 8; y++) { + pOut1 = pOut; + for (x = 0; x < 8; x++) + *pOut1++ = *pIn++; + pOut += w; + } +} + +#if 0 +/* + * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ + static void +yuv400raw_to_yuv400p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int x, y; + unsigned char *pIn, *pOut, *pOutLine; + + /* Copy Y */ + pIn = pIn0; + pOutLine = pOut0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + } + pOutLine += 8 * frame->rawwidth; + } +} +#endif + +/* + * For YUV 4:2:0 images, the data show up in 384 byte segments. + * The first 64 bytes of each segment are U, the next 64 are V. The U and + * V are arranged as follows: + * + * 0 1 ... 7 + * 8 9 ... 15 + * ... + * 56 57 ... 63 + * + * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + * + * The next 256 bytes are full resolution Y data and represent 4 squares + * of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 + * + * Note that the U and V data in one segment represent a 16 x 16 pixel + * area, but the Y data represent a 32 x 8 pixel area. If the width is not an + * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the + * next horizontal stripe. + * + * If dumppix module param is set, _parse_data just dumps the incoming segments, + * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 + * this puts the data on the standard output and can be analyzed with the + * parseppm.c utility I wrote. That's a much faster way for figuring out how + * these data are scrambled. + */ + +/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0. + * + * FIXME: Currently only handles width and height that are multiples of 16 + */ + static void +yuv420raw_to_yuv420p(unsigned char *pIn0, unsigned char *pOut0, + int width, int height) +{ + int k, x, y; + unsigned char *pIn, *pOut, *pOutLine; + const unsigned int a = width * height; + const unsigned int w = width / 2; + + /* Copy U and V */ + pIn = pIn0; + pOutLine = pOut0 + a; + for (y = 0; y < height - 1; y += 16) { + pOut = pOutLine; + for (x = 0; x < width - 1; x += 16) { + make_8x8(pIn, pOut, w); + make_8x8(pIn + 64, pOut + a / 4, w); + pIn += 384; + pOut += 8; + } + pOutLine += 8 * w; + } + + /* Copy Y */ + pIn = pIn0 + 128; + pOutLine = pOut0; + k = 0; + for (y = 0; y < height - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < width - 1; x += 8) { + make_8x8(pIn, pOut, width); + pIn += 64; + pOut += 8; + if ((++k) > 3) { + k = 0; + pIn += 128; + } + } + pOutLine += 8 * width; + } +} + + +/* Remove all 0 blocks from input */ +static void remove0blocks(unsigned char *pIn, int *inSize) +{ + long long *in = (long long *)pIn; + long long *out = (long long *)pIn; + int i, j; + + for (i = 0; i < *inSize; i += 32, in += 4) { + int all_zero = 1; + for (j = 0; j < 4; j++) + if (in[j]) { + all_zero = 0; + break; + } + + /* Skip 32 byte blocks of all 0 */ + if (all_zero) + continue; + + for (j = 0; j < 4; j++) + *out++ = in[j]; + } + + *inSize -= (in - out) * 8; +} + +static int v4lconvert_ov511_to_yuv420(unsigned char *src, unsigned char *dest, + int w, int h, int yvu, int src_size) +{ + int rc = 0; + + src_size -= 11; /* Remove footer */ + + remove0blocks(src, &src_size); + + /* Compressed ? */ + if (src[8] & 0x40) + rc = Decompress420HiNoMMX(src + 9, dest, w, h, src_size); + else + yuv420raw_to_yuv420p(src + 9, dest, w, h); + + return rc; +} + +int main(int argc, char *argv[]) +{ + int width, height, yvu, src_size, dest_size; + unsigned char src_buf[500000]; + unsigned char dest_buf[500000]; + + while (1) { + if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (src_size > sizeof(src_buf)) { + fprintf(stderr, "%s: error: src_buf too small, need: %d\n", + argv[0], src_size); + return 2; + } + + if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + + dest_size = width * height * 3 / 2; + if (width <= 0 || width > SHRT_MAX || height <= 0 || height > SHRT_MAX) { + fprintf(stderr, "%s: error: width or height out of bounds\n", + argv[0]); + dest_size = -1; + } else if (dest_size > sizeof(dest_buf)) { + fprintf(stderr, "%s: error: dest_buf too small, need: %d\n", + argv[0], dest_size); + dest_size = -1; + } else if (v4lconvert_ov511_to_yuv420(src_buf, dest_buf, width, height, + yvu, src_size)) + dest_size = -1; + + if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int), + argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (dest_size == -1) + continue; + + if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + } +} diff --git a/libv4lconvert/ov518-decomp.c b/libv4lconvert/ov518-decomp.c new file mode 100644 index 0000000..91d908c --- /dev/null +++ b/libv4lconvert/ov518-decomp.c @@ -0,0 +1,1480 @@ +/* We would like to embed this inside libv4l, but we cannot as I've failed + to contact Mark W. McClelland to get permission to relicense this, + so this lives in an external (GPL licensed) helper */ + +/* OV518 Decompression Support Module (No-MMX version) + * + * Copyright (c) 2002-2003 Mark W. McClelland. All rights reserved. + * http://alpha.dyndns.org/ov511/ + * + * Fast integer iDCT by Yuri van Oers + * Original OV511 decompression code Copyright 1998-2000 OmniVision Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include "helper-funcs.h" + +/****************************************************************************** + * Compile-time Options + ******************************************************************************/ + +/* Defining APPROXIMATE_MUL_BY_SHIFT increases performance by approximation + * the multiplications by shifts. I think there's no change in the + * calculated picture, but I'm not sure, so the choice is still in here. */ +#undef APPROXIMATE_MUL_BY_SHIFT + +/****************************************************************************** + * Local Data Types + ******************************************************************************/ + +/* Make sure this remains naturally aligned and 2^n bytes in size */ +struct tree_node { + short left; /* Pointer to left child node */ + short right; /* Pointer to right child node */ + signed char depth; /* Depth (starting at 1) if leaf, else -1 */ + signed char coeffbits; /* Size of coefficient data, or zero if none */ + signed char skip; /* Number of zero coefficients. Unused w/ DC */ + char padding; /* Pad out to 8 bytes */ +}; + +struct comp_info { + int bytes; /* Number of processed input bytes */ + int bits; /* Number of unprocessed input bits */ + int rawLen; /* Total number of bytes in input buffer */ + unsigned char *qt; /* Current quantization table */ +}; + +/****************************************************************************** + * Constant Data Definitions + ******************************************************************************/ + +/* Zig-Zag Table */ +static const unsigned char ZigZag518[] = { + 0x00, 0x02, 0x03, 0x09, + 0x01, 0x04, 0x08, 0x0a, + 0x05, 0x07, 0x0b, 0x11, + 0x06, 0x0c, 0x10, 0x12, + 0x0d, 0x0f, 0x13, 0x19, + 0x0e, 0x14, 0x18, 0x1a, + 0x15, 0x17, 0x1b, 0x1e, + 0x16, 0x1c, 0x1d, 0x1f +}; + +/* Huffman trees */ + +static const struct tree_node treeYAC[] = { + { 1, 4, -1, 0, -1}, { 2, 3, -1, 0, -1}, + { -1, -1, 2, 1, 0}, { -1, -1, 2, 2, 0}, + { 5, 9, -1, 0, -1}, { 6, 7, -1, 0, -1}, + { -1, -1, 3, 3, 0}, {323, 8, -1, 0, -1}, + { -1, -1, 4, 4, 0}, { 10, 13, -1, 0, -1}, + { 38, 11, -1, 0, -1}, { 12, 39, -1, 0, -1}, + { -1, -1, 5, 5, 0}, { 59, 14, -1, 0, -1}, + { 15, 18, -1, 0, -1}, { 16, 113, -1, 0, -1}, + { 17, 40, -1, 0, -1}, { -1, -1, 7, 6, 0}, + { 19, 22, -1, 0, -1}, { 20, 41, -1, 0, -1}, + { 21, 61, -1, 0, -1}, { -1, -1, 8, 7, 0}, + { 23, 27, -1, 0, -1}, {169, 24, -1, 0, -1}, + {208, 25, -1, 0, -1}, { 26, 62, -1, 0, -1}, + { -1, -1, 10, 8, 0}, { 44, 28, -1, 0, -1}, + { 63, 29, -1, 0, -1}, { 30, 191, -1, 0, -1}, + { 31, 119, -1, 0, -1}, { 32, 82, -1, 0, -1}, + { 33, 55, -1, 0, -1}, { 34, 48, -1, 0, -1}, + {171, 35, -1, 0, -1}, { 36, 37, -1, 0, -1}, + { -1, -1, 16, 9, 0}, { -1, -1, 16, 10, 0}, + { -1, -1, 4, 1, 1}, { -1, -1, 5, 2, 1}, + { -1, -1, 7, 3, 1}, {151, 42, -1, 0, -1}, + { 43, 79, -1, 0, -1}, { -1, -1, 9, 4, 1}, + { 96, 45, -1, 0, -1}, {246, 46, -1, 0, -1}, + { 47, 115, -1, 0, -1}, { -1, -1, 11, 5, 1}, + { 49, 52, -1, 0, -1}, { 50, 51, -1, 0, -1}, + { -1, -1, 16, 6, 1}, { -1, -1, 16, 7, 1}, + { 53, 54, -1, 0, -1}, { -1, -1, 16, 8, 1}, + { -1, -1, 16, 9, 1}, { 56, 71, -1, 0, -1}, + { 57, 68, -1, 0, -1}, { 58, 67, -1, 0, -1}, + { -1, -1, 16, 10, 1}, { 60, 77, -1, 0, -1}, + { -1, -1, 5, 1, 2}, { -1, -1, 8, 2, 2}, + { -1, -1, 10, 3, 2}, {265, 64, -1, 0, -1}, + { 65, 134, -1, 0, -1}, { 66, 80, -1, 0, -1}, + { -1, -1, 12, 4, 2}, { -1, -1, 16, 5, 2}, + { 69, 70, -1, 0, -1}, { -1, -1, 16, 6, 2}, + { -1, -1, 16, 7, 2}, { 72, 75, -1, 0, -1}, + { 73, 74, -1, 0, -1}, { -1, -1, 16, 8, 2}, + { -1, -1, 16, 9, 2}, { 76, 81, -1, 0, -1}, + { -1, -1, 16, 10, 2}, { 78, 95, -1, 0, -1}, + { -1, -1, 6, 1, 3}, { -1, -1, 9, 2, 3}, + { -1, -1, 12, 3, 3}, { -1, -1, 16, 4, 3}, + { 83, 101, -1, 0, -1}, { 84, 91, -1, 0, -1}, + { 85, 88, -1, 0, -1}, { 86, 87, -1, 0, -1}, + { -1, -1, 16, 5, 3}, { -1, -1, 16, 6, 3}, + { 89, 90, -1, 0, -1}, { -1, -1, 16, 7, 3}, + { -1, -1, 16, 8, 3}, { 92, 98, -1, 0, -1}, + { 93, 94, -1, 0, -1}, { -1, -1, 16, 9, 3}, + { -1, -1, 16, 10, 3}, { -1, -1, 6, 1, 4}, + { 97, 225, -1, 0, -1}, { -1, -1, 10, 2, 4}, + { 99, 100, -1, 0, -1}, { -1, -1, 16, 3, 4}, + { -1, -1, 16, 4, 4}, {102, 109, -1, 0, -1}, + {103, 106, -1, 0, -1}, {104, 105, -1, 0, -1}, + { -1, -1, 16, 5, 4}, { -1, -1, 16, 6, 4}, + {107, 108, -1, 0, -1}, { -1, -1, 16, 7, 4}, + { -1, -1, 16, 8, 4}, {110, 116, -1, 0, -1}, + {111, 112, -1, 0, -1}, { -1, -1, 16, 9, 4}, + { -1, -1, 16, 10, 4}, {114, 133, -1, 0, -1}, + { -1, -1, 7, 1, 5}, { -1, -1, 11, 2, 5}, + {117, 118, -1, 0, -1}, { -1, -1, 16, 3, 5}, + { -1, -1, 16, 4, 5}, {120, 156, -1, 0, -1}, + {121, 139, -1, 0, -1}, {122, 129, -1, 0, -1}, + {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1}, + { -1, -1, 16, 5, 5}, { -1, -1, 16, 6, 5}, + {127, 128, -1, 0, -1}, { -1, -1, 16, 7, 5}, + { -1, -1, 16, 8, 5}, {130, 136, -1, 0, -1}, + {131, 132, -1, 0, -1}, { -1, -1, 16, 9, 5}, + { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6}, + {135, 152, -1, 0, -1}, { -1, -1, 12, 2, 6}, + {137, 138, -1, 0, -1}, { -1, -1, 16, 3, 6}, + { -1, -1, 16, 4, 6}, {140, 147, -1, 0, -1}, + {141, 144, -1, 0, -1}, {142, 143, -1, 0, -1}, + { -1, -1, 16, 5, 6}, { -1, -1, 16, 6, 6}, + {145, 146, -1, 0, -1}, { -1, -1, 16, 7, 6}, + { -1, -1, 16, 8, 6}, {148, 153, -1, 0, -1}, + {149, 150, -1, 0, -1}, { -1, -1, 16, 9, 6}, + { -1, -1, 16, 10, 6}, { -1, -1, 8, 1, 7}, + { -1, -1, 12, 2, 7}, {154, 155, -1, 0, -1}, + { -1, -1, 16, 3, 7}, { -1, -1, 16, 4, 7}, + {157, 175, -1, 0, -1}, {158, 165, -1, 0, -1}, + {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1}, + { -1, -1, 16, 5, 7}, { -1, -1, 16, 6, 7}, + {163, 164, -1, 0, -1}, { -1, -1, 16, 7, 7}, + { -1, -1, 16, 8, 7}, {166, 172, -1, 0, -1}, + {167, 168, -1, 0, -1}, { -1, -1, 16, 9, 7}, + { -1, -1, 16, 10, 7}, {170, 187, -1, 0, -1}, + { -1, -1, 9, 1, 8}, { -1, -1, 15, 2, 8}, + {173, 174, -1, 0, -1}, { -1, -1, 16, 3, 8}, + { -1, -1, 16, 4, 8}, {176, 183, -1, 0, -1}, + {177, 180, -1, 0, -1}, {178, 179, -1, 0, -1}, + { -1, -1, 16, 5, 8}, { -1, -1, 16, 6, 8}, + {181, 182, -1, 0, -1}, { -1, -1, 16, 7, 8}, + { -1, -1, 16, 8, 8}, {184, 188, -1, 0, -1}, + {185, 186, -1, 0, -1}, { -1, -1, 16, 9, 8}, + { -1, -1, 16, 10, 8}, { -1, -1, 9, 1, 9}, + {189, 190, -1, 0, -1}, { -1, -1, 16, 2, 9}, + { -1, -1, 16, 3, 9}, {192, 258, -1, 0, -1}, + {193, 226, -1, 0, -1}, {194, 210, -1, 0, -1}, + {195, 202, -1, 0, -1}, {196, 199, -1, 0, -1}, + {197, 198, -1, 0, -1}, { -1, -1, 16, 4, 9}, + { -1, -1, 16, 5, 9}, {200, 201, -1, 0, -1}, + { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9}, + {203, 206, -1, 0, -1}, {204, 205, -1, 0, -1}, + { -1, -1, 16, 8, 9}, { -1, -1, 16, 9, 9}, + {207, 209, -1, 0, -1}, { -1, -1, 16, 10, 9}, + { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10}, + {211, 218, -1, 0, -1}, {212, 215, -1, 0, -1}, + {213, 214, -1, 0, -1}, { -1, -1, 16, 3, 10}, + { -1, -1, 16, 4, 10}, {216, 217, -1, 0, -1}, + { -1, -1, 16, 5, 10}, { -1, -1, 16, 6, 10}, + {219, 222, -1, 0, -1}, {220, 221, -1, 0, -1}, + { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10}, + {223, 224, -1, 0, -1}, { -1, -1, 16, 9, 10}, + { -1, -1, 16, 10, 10}, { -1, -1, 10, 1, 11}, + {227, 242, -1, 0, -1}, {228, 235, -1, 0, -1}, + {229, 232, -1, 0, -1}, {230, 231, -1, 0, -1}, + { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11}, + {233, 234, -1, 0, -1}, { -1, -1, 16, 4, 11}, + { -1, -1, 16, 5, 11}, {236, 239, -1, 0, -1}, + {237, 238, -1, 0, -1}, { -1, -1, 16, 6, 11}, + { -1, -1, 16, 7, 11}, {240, 241, -1, 0, -1}, + { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11}, + {243, 251, -1, 0, -1}, {244, 248, -1, 0, -1}, + {245, 247, -1, 0, -1}, { -1, -1, 16, 10, 11}, + { -1, -1, 10, 1, 12}, { -1, -1, 16, 2, 12}, + {249, 250, -1, 0, -1}, { -1, -1, 16, 3, 12}, + { -1, -1, 16, 4, 12}, {252, 255, -1, 0, -1}, + {253, 254, -1, 0, -1}, { -1, -1, 16, 5, 12}, + { -1, -1, 16, 6, 12}, {256, 257, -1, 0, -1}, + { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12}, + {259, 292, -1, 0, -1}, {260, 277, -1, 0, -1}, + {261, 270, -1, 0, -1}, {262, 267, -1, 0, -1}, + {263, 264, -1, 0, -1}, { -1, -1, 16, 9, 12}, + { -1, -1, 16, 10, 12}, {266, 322, -1, 0, -1}, + { -1, -1, 11, 1, 13}, {268, 269, -1, 0, -1}, + { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13}, + {271, 274, -1, 0, -1}, {272, 273, -1, 0, -1}, + { -1, -1, 16, 4, 13}, { -1, -1, 16, 5, 13}, + {275, 276, -1, 0, -1}, { -1, -1, 16, 6, 13}, + { -1, -1, 16, 7, 13}, {278, 285, -1, 0, -1}, + {279, 282, -1, 0, -1}, {280, 281, -1, 0, -1}, + { -1, -1, 16, 8, 13}, { -1, -1, 16, 9, 13}, + {283, 284, -1, 0, -1}, { -1, -1, 16, 10, 13}, + { -1, -1, 16, 1, 14}, {286, 289, -1, 0, -1}, + {287, 288, -1, 0, -1}, { -1, -1, 16, 2, 14}, + { -1, -1, 16, 3, 14}, {290, 291, -1, 0, -1}, + { -1, -1, 16, 4, 14}, { -1, -1, 16, 5, 14}, + {293, 308, -1, 0, -1}, {294, 301, -1, 0, -1}, + {295, 298, -1, 0, -1}, {296, 297, -1, 0, -1}, + { -1, -1, 16, 6, 14}, { -1, -1, 16, 7, 14}, + {299, 300, -1, 0, -1}, { -1, -1, 16, 8, 14}, + { -1, -1, 16, 9, 14}, {302, 305, -1, 0, -1}, + {303, 304, -1, 0, -1}, { -1, -1, 16, 10, 14}, + { -1, -1, 16, 1, 15}, {306, 307, -1, 0, -1}, + { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15}, + {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1}, + {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15}, + { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1}, + { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15}, + {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1}, + { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15}, + {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15}, + { -1, -1, 11, 0, 16}, { -1, -1, 4, 0, -1}, +}; + +static const struct tree_node treeUVAC[] = { + { 1, 3, -1, 0, -1}, {323, 2, -1, 0, -1}, + { -1, -1, 2, 1, 0}, { 4, 8, -1, 0, -1}, + { 5, 6, -1, 0, -1}, { -1, -1, 3, 2, 0}, + { 7, 37, -1, 0, -1}, { -1, -1, 4, 3, 0}, + { 9, 13, -1, 0, -1}, { 10, 60, -1, 0, -1}, + { 11, 12, -1, 0, -1}, { -1, -1, 5, 4, 0}, + { -1, -1, 5, 5, 0}, { 14, 17, -1, 0, -1}, + { 15, 97, -1, 0, -1}, { 16, 38, -1, 0, -1}, + { -1, -1, 6, 6, 0}, { 18, 21, -1, 0, -1}, + { 19, 39, -1, 0, -1}, { 20, 135, -1, 0, -1}, + { -1, -1, 7, 7, 0}, { 22, 26, -1, 0, -1}, + { 82, 23, -1, 0, -1}, { 24, 99, -1, 0, -1}, + { 25, 42, -1, 0, -1}, { -1, -1, 9, 8, 0}, + { 27, 31, -1, 0, -1}, {211, 28, -1, 0, -1}, + {248, 29, -1, 0, -1}, { 30, 63, -1, 0, -1}, + { -1, -1, 10, 9, 0}, { 43, 32, -1, 0, -1}, + { 33, 48, -1, 0, -1}, {153, 34, -1, 0, -1}, + { 35, 64, -1, 0, -1}, { 36, 47, -1, 0, -1}, + { -1, -1, 12, 10, 0}, { -1, -1, 4, 1, 1}, + { -1, -1, 6, 2, 1}, {152, 40, -1, 0, -1}, + { 41, 62, -1, 0, -1}, { -1, -1, 8, 3, 1}, + { -1, -1, 9, 4, 1}, { 84, 44, -1, 0, -1}, + {322, 45, -1, 0, -1}, { 46, 136, -1, 0, -1}, + { -1, -1, 11, 5, 1}, { -1, -1, 12, 6, 1}, + { 49, 189, -1, 0, -1}, { 50, 119, -1, 0, -1}, + { 51, 76, -1, 0, -1}, { 66, 52, -1, 0, -1}, + { 53, 69, -1, 0, -1}, { 54, 57, -1, 0, -1}, + { 55, 56, -1, 0, -1}, { -1, -1, 16, 7, 1}, + { -1, -1, 16, 8, 1}, { 58, 59, -1, 0, -1}, + { -1, -1, 16, 9, 1}, { -1, -1, 16, 10, 1}, + { 61, 81, -1, 0, -1}, { -1, -1, 5, 1, 2}, + { -1, -1, 8, 2, 2}, { -1, -1, 10, 3, 2}, + { 65, 86, -1, 0, -1}, { -1, -1, 12, 4, 2}, + {286, 67, -1, 0, -1}, { 68, 304, -1, 0, -1}, + { -1, -1, 15, 5, 2}, { 70, 73, -1, 0, -1}, + { 71, 72, -1, 0, -1}, { -1, -1, 16, 6, 2}, + { -1, -1, 16, 7, 2}, { 74, 75, -1, 0, -1}, + { -1, -1, 16, 8, 2}, { -1, -1, 16, 9, 2}, + { 77, 102, -1, 0, -1}, { 78, 91, -1, 0, -1}, + { 79, 88, -1, 0, -1}, { 80, 87, -1, 0, -1}, + { -1, -1, 16, 10, 2}, { -1, -1, 5, 1, 3}, + { 83, 171, -1, 0, -1}, { -1, -1, 8, 2, 3}, + { 85, 117, -1, 0, -1}, { -1, -1, 10, 3, 3}, + { -1, -1, 12, 4, 3}, { -1, -1, 16, 5, 3}, + { 89, 90, -1, 0, -1}, { -1, -1, 16, 6, 3}, + { -1, -1, 16, 7, 3}, { 92, 95, -1, 0, -1}, + { 93, 94, -1, 0, -1}, { -1, -1, 16, 8, 3}, + { -1, -1, 16, 9, 3}, { 96, 101, -1, 0, -1}, + { -1, -1, 16, 10, 3}, { 98, 116, -1, 0, -1}, + { -1, -1, 6, 1, 4}, {100, 188, -1, 0, -1}, + { -1, -1, 9, 2, 4}, { -1, -1, 16, 3, 4}, + {103, 110, -1, 0, -1}, {104, 107, -1, 0, -1}, + {105, 106, -1, 0, -1}, { -1, -1, 16, 4, 4}, + { -1, -1, 16, 5, 4}, {108, 109, -1, 0, -1}, + { -1, -1, 16, 6, 4}, { -1, -1, 16, 7, 4}, + {111, 114, -1, 0, -1}, {112, 113, -1, 0, -1}, + { -1, -1, 16, 8, 4}, { -1, -1, 16, 9, 4}, + {115, 118, -1, 0, -1}, { -1, -1, 16, 10, 4}, + { -1, -1, 6, 1, 5}, { -1, -1, 10, 2, 5}, + { -1, -1, 16, 3, 5}, {120, 156, -1, 0, -1}, + {121, 138, -1, 0, -1}, {122, 129, -1, 0, -1}, + {123, 126, -1, 0, -1}, {124, 125, -1, 0, -1}, + { -1, -1, 16, 4, 5}, { -1, -1, 16, 5, 5}, + {127, 128, -1, 0, -1}, { -1, -1, 16, 6, 5}, + { -1, -1, 16, 7, 5}, {130, 133, -1, 0, -1}, + {131, 132, -1, 0, -1}, { -1, -1, 16, 8, 5}, + { -1, -1, 16, 9, 5}, {134, 137, -1, 0, -1}, + { -1, -1, 16, 10, 5}, { -1, -1, 7, 1, 6}, + { -1, -1, 11, 2, 6}, { -1, -1, 16, 3, 6}, + {139, 146, -1, 0, -1}, {140, 143, -1, 0, -1}, + {141, 142, -1, 0, -1}, { -1, -1, 16, 4, 6}, + { -1, -1, 16, 5, 6}, {144, 145, -1, 0, -1}, + { -1, -1, 16, 6, 6}, { -1, -1, 16, 7, 6}, + {147, 150, -1, 0, -1}, {148, 149, -1, 0, -1}, + { -1, -1, 16, 8, 6}, { -1, -1, 16, 9, 6}, + {151, 155, -1, 0, -1}, { -1, -1, 16, 10, 6}, + { -1, -1, 7, 1, 7}, {154, 267, -1, 0, -1}, + { -1, -1, 11, 2, 7}, { -1, -1, 16, 3, 7}, + {157, 173, -1, 0, -1}, {158, 165, -1, 0, -1}, + {159, 162, -1, 0, -1}, {160, 161, -1, 0, -1}, + { -1, -1, 16, 4, 7}, { -1, -1, 16, 5, 7}, + {163, 164, -1, 0, -1}, { -1, -1, 16, 6, 7}, + { -1, -1, 16, 7, 7}, {166, 169, -1, 0, -1}, + {167, 168, -1, 0, -1}, { -1, -1, 16, 8, 7}, + { -1, -1, 16, 9, 7}, {170, 172, -1, 0, -1}, + { -1, -1, 16, 10, 7}, { -1, -1, 8, 1, 8}, + { -1, -1, 16, 2, 8}, {174, 181, -1, 0, -1}, + {175, 178, -1, 0, -1}, {176, 177, -1, 0, -1}, + { -1, -1, 16, 3, 8}, { -1, -1, 16, 4, 8}, + {179, 180, -1, 0, -1}, { -1, -1, 16, 5, 8}, + { -1, -1, 16, 6, 8}, {182, 185, -1, 0, -1}, + {183, 184, -1, 0, -1}, { -1, -1, 16, 7, 8}, + { -1, -1, 16, 8, 8}, {186, 187, -1, 0, -1}, + { -1, -1, 16, 9, 8}, { -1, -1, 16, 10, 8}, + { -1, -1, 9, 1, 9}, {190, 257, -1, 0, -1}, + {191, 224, -1, 0, -1}, {192, 207, -1, 0, -1}, + {193, 200, -1, 0, -1}, {194, 197, -1, 0, -1}, + {195, 196, -1, 0, -1}, { -1, -1, 16, 2, 9}, + { -1, -1, 16, 3, 9}, {198, 199, -1, 0, -1}, + { -1, -1, 16, 4, 9}, { -1, -1, 16, 5, 9}, + {201, 204, -1, 0, -1}, {202, 203, -1, 0, -1}, + { -1, -1, 16, 6, 9}, { -1, -1, 16, 7, 9}, + {205, 206, -1, 0, -1}, { -1, -1, 16, 8, 9}, + { -1, -1, 16, 9, 9}, {208, 217, -1, 0, -1}, + {209, 214, -1, 0, -1}, {210, 213, -1, 0, -1}, + { -1, -1, 16, 10, 9}, {212, 230, -1, 0, -1}, + { -1, -1, 9, 1, 10}, { -1, -1, 16, 2, 10}, + {215, 216, -1, 0, -1}, { -1, -1, 16, 3, 10}, + { -1, -1, 16, 4, 10}, {218, 221, -1, 0, -1}, + {219, 220, -1, 0, -1}, { -1, -1, 16, 5, 10}, + { -1, -1, 16, 6, 10}, {222, 223, -1, 0, -1}, + { -1, -1, 16, 7, 10}, { -1, -1, 16, 8, 10}, + {225, 241, -1, 0, -1}, {226, 234, -1, 0, -1}, + {227, 231, -1, 0, -1}, {228, 229, -1, 0, -1}, + { -1, -1, 16, 9, 10}, { -1, -1, 16, 10, 10}, + { -1, -1, 9, 1, 11}, {232, 233, -1, 0, -1}, + { -1, -1, 16, 2, 11}, { -1, -1, 16, 3, 11}, + {235, 238, -1, 0, -1}, {236, 237, -1, 0, -1}, + { -1, -1, 16, 4, 11}, { -1, -1, 16, 5, 11}, + {239, 240, -1, 0, -1}, { -1, -1, 16, 6, 11}, + { -1, -1, 16, 7, 11}, {242, 250, -1, 0, -1}, + {243, 246, -1, 0, -1}, {244, 245, -1, 0, -1}, + { -1, -1, 16, 8, 11}, { -1, -1, 16, 9, 11}, + {247, 249, -1, 0, -1}, { -1, -1, 16, 10, 11}, + { -1, -1, 9, 1, 12}, { -1, -1, 16, 2, 12}, + {251, 254, -1, 0, -1}, {252, 253, -1, 0, -1}, + { -1, -1, 16, 3, 12}, { -1, -1, 16, 4, 12}, + {255, 256, -1, 0, -1}, { -1, -1, 16, 5, 12}, + { -1, -1, 16, 6, 12}, {258, 291, -1, 0, -1}, + {259, 275, -1, 0, -1}, {260, 268, -1, 0, -1}, + {261, 264, -1, 0, -1}, {262, 263, -1, 0, -1}, + { -1, -1, 16, 7, 12}, { -1, -1, 16, 8, 12}, + {265, 266, -1, 0, -1}, { -1, -1, 16, 9, 12}, + { -1, -1, 16, 10, 12}, { -1, -1, 11, 1, 13}, + {269, 272, -1, 0, -1}, {270, 271, -1, 0, -1}, + { -1, -1, 16, 2, 13}, { -1, -1, 16, 3, 13}, + {273, 274, -1, 0, -1}, { -1, -1, 16, 4, 13}, + { -1, -1, 16, 5, 13}, {276, 283, -1, 0, -1}, + {277, 280, -1, 0, -1}, {278, 279, -1, 0, -1}, + { -1, -1, 16, 6, 13}, { -1, -1, 16, 7, 13}, + {281, 282, -1, 0, -1}, { -1, -1, 16, 8, 13}, + { -1, -1, 16, 9, 13}, {284, 288, -1, 0, -1}, + {285, 287, -1, 0, -1}, { -1, -1, 16, 10, 13}, + { -1, -1, 14, 1, 14}, { -1, -1, 16, 2, 14}, + {289, 290, -1, 0, -1}, { -1, -1, 16, 3, 14}, + { -1, -1, 16, 4, 14}, {292, 308, -1, 0, -1}, + {293, 300, -1, 0, -1}, {294, 297, -1, 0, -1}, + {295, 296, -1, 0, -1}, { -1, -1, 16, 5, 14}, + { -1, -1, 16, 6, 14}, {298, 299, -1, 0, -1}, + { -1, -1, 16, 7, 14}, { -1, -1, 16, 8, 14}, + {301, 305, -1, 0, -1}, {302, 303, -1, 0, -1}, + { -1, -1, 16, 9, 14}, { -1, -1, 16, 10, 14}, + { -1, -1, 15, 1, 15}, {306, 307, -1, 0, -1}, + { -1, -1, 16, 2, 15}, { -1, -1, 16, 3, 15}, + {309, 316, -1, 0, -1}, {310, 313, -1, 0, -1}, + {311, 312, -1, 0, -1}, { -1, -1, 16, 4, 15}, + { -1, -1, 16, 5, 15}, {314, 315, -1, 0, -1}, + { -1, -1, 16, 6, 15}, { -1, -1, 16, 7, 15}, + {317, 320, -1, 0, -1}, {318, 319, -1, 0, -1}, + { -1, -1, 16, 8, 15}, { -1, -1, 16, 9, 15}, + {321, -1, -1, 0, -1}, { -1, -1, 16, 10, 15}, + { -1, -1, 10, 0, 16}, { -1, -1, 2, 0, -1}, +}; + +static const struct tree_node treeYDC[] = { + { 1, 6, -1, 0}, { 2, 3, -1, 0}, + { -1, -1, 2, 0}, { 4, 5, -1, 0}, + { -1, -1, 3, 1}, { -1, -1, 3, 2}, + { 7, 10, -1, 0}, { 8, 9, -1, 0}, + { -1, -1, 3, 3}, { -1, -1, 3, 4}, + { 11, 12, -1, 0}, { -1, -1, 3, 5}, + { 13, 14, -1, 0}, { -1, -1, 4, 6}, + { 15, 16, -1, 0}, { -1, -1, 5, 7}, + { 17, 18, -1, 0}, { -1, -1, 6, 8}, + { 19, 20, -1, 0}, { -1, -1, 7, 9}, + { 21, 22, -1, 0}, { -1, -1, 8, 10}, + { 23, -1, -1, 0}, { -1, -1, 9, 11}, +}; + +static const struct tree_node treeUVDC[] = { + { 1, 4, -1, 0}, { 2, 3, -1, 0}, + { -1, -1, 2, 0}, { -1, -1, 2, 1}, + { 5, 6, -1, 0}, { -1, -1, 2, 2}, + { 7, 8, -1, 0}, { -1, -1, 3, 3}, + { 9, 10, -1, 0}, { -1, -1, 4, 4}, + { 11, 12, -1, 0}, { -1, -1, 5, 5}, + { 13, 14, -1, 0}, { -1, -1, 6, 6}, + { 15, 16, -1, 0}, { -1, -1, 7, 7}, + { 17, 18, -1, 0}, { -1, -1, 8, 8}, + { 19, 20, -1, 0}, { -1, -1, 9, 9}, + { 21, 22, -1, 0}, { -1, -1, 10, 10}, + { 23, -1, -1, 0}, { -1, -1, 11, 11}, +}; + +/****************************************************************************** + * Huffman Decoder + ******************************************************************************/ + +/* Note: There is no penalty for passing the tree as an argument, since dummy + * args are passed anyway (to maintain 16-byte stack alignment), and since the + * address is loaded into a register either way. */ + +/* If no node is found, coeffbits and skip will not be modified */ +/* Return: Depth of node found, or -1 if invalid input code */ +static int +getNodeAC(unsigned int in, signed char *coeffbits, signed char *skip, + const struct tree_node *tree) +{ + int node = 0; + int i = 0; + int depth; + + do { + if ((in & 0x80000000) == 0) + node = tree[node].left; + else + node = tree[node].right; + + if (node == -1) + break; + + depth = tree[node].depth; + + /* Is it a leaf? If not, branch downward */ + if (depth != -1) { + *coeffbits = tree[node].coeffbits; + *skip = tree[node].skip; + return depth; + } + + in <<= 1; + ++i; + } while (i <= 15); + + return -1; +} + +/* If no node is found, coeffbits will not be modified */ +/* Return: Depth of node found, or -1 if invalid input code */ +static int +getNodeDC(unsigned int in, signed char *coeffbits, const struct tree_node *tree) +{ + int node = 0; + int i = 0; + int depth; + + do { + if ((in & 0x80000000) == 0) + node = tree[node].left; + else + node = tree[node].right; + + if (node == -1) + break; + + depth = tree[node].depth; + + /* Is it a leaf? If not, branch downward */ + if (depth != -1) { + *coeffbits = tree[node].coeffbits; + return depth; + } + + in <<= 1; + ++i; + } while (i <= 15); + + return -1; +} + +static inline unsigned int +getBytes(int *rawData, struct comp_info *cinfo) +{ + int bufLen = cinfo->rawLen; + int bits = cinfo->bits; + int bytes = cinfo->bytes; + unsigned char *in = bytes + (unsigned char *) rawData; + unsigned char b1, b2, b3, b4, b5; + unsigned int packedIn; + + /* Pull 5 bytes out of raw data */ + if (bytes < bufLen - 4) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + b4 = in[3]; + b5 = in[4]; + } else { + if (bytes < bufLen - 3) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + b4 = in[3]; + } else { + if (bytes < bufLen - 2) { + b1 = in[0]; + b2 = in[1]; + b3 = in[2]; + } else { + if (bytes < bufLen - 1) { + b1 = in[0]; + b2 = in[1]; + } else { + if (bytes <= bufLen) + b1 = in[0]; + else + b1 = 0; + b2 = 0; + } + b3 = 0; + } + b4 = 0; + } + b5 = 0; + } + + /* Pack the bytes */ + packedIn = b1 << 24; + packedIn += b2 << 16; + packedIn += b3 << 8; + packedIn += b4; + + if (bits != 0) { + packedIn = packedIn << bits; + packedIn += b5 >> (8 - bits); + } + + return packedIn; +} + +static int +getACCoefficient(int *rawData, int *coeff, struct comp_info *cinfo, + const struct tree_node *tree) +{ + int input, bits, bytes, tmp_c; + signed char coeffbits = 0; + signed char skip = 0; + + input = getBytes(rawData, cinfo); + bits = getNodeAC(input, &coeffbits, &skip, tree); + + if (coeffbits) { + input = input << (bits - 1); + input &= 0x7fffffff; + if (!(input & 0x40000000)) + input |= 0x80000000; + + tmp_c = input >> (31 - coeffbits); + if (tmp_c < 0) + tmp_c++; + *coeff = tmp_c; + + bits += coeffbits; + } + + bytes = (bits + cinfo->bits) >> 3; + cinfo->bytes += bytes; + cinfo->bits += bits - (bytes << 3); + + return skip; +} + +static void +getDCCoefficient(int *rawData, int *coeff, struct comp_info *cinfo, + const struct tree_node *tree) +{ + int input, bits, bytes, tmp_c; + signed char coeffbits = 0; + + input = getBytes(rawData, cinfo); + bits = getNodeDC(input, &coeffbits, tree); + + if (bits == -1) { + bits = 1; /* Try to re-sync at the next bit */ + *coeff = 0; /* Indicates no change from last DC */ + } else { + + input = input << (bits - 1); + input &= 0x7fffffff; + if (!(input & 0x40000000)) + input |= 0x80000000; + + tmp_c = input >> (31 - coeffbits); + if (tmp_c < 0) + tmp_c++; + *coeff = tmp_c; + + bits += coeffbits; + } + + bytes = (bits + cinfo->bits) >> 3; + cinfo->bytes += bytes; + cinfo->bits += bits - (bytes << 3); +} + +/* For AC coefficients, here is what the "skip" value means: + * -1: Either the 8x4 block has ended, or the decoding failed. + * 0: Use the returned coeff. Don't skip anything. + * 1-15: The next coeffs are zero. The returned coeff is used. + * 16: The next 16 coeffs are zero. The returned coeff is ignored. + * + * You must ensure that the C[] array not be overrun, or stack corruption will + * result. + */ +static void +huffmanDecoderY(int *C, int *pIn, struct comp_info *cinfo) +{ + int coeff = 0; + int i = 1; + int k, skip; + + getDCCoefficient(pIn, C, cinfo, treeYDC); + + i = 1; + do { + skip = getACCoefficient(pIn, &coeff, cinfo, treeYAC); + + if (skip == -1) { + break; + } else if (skip == 0) { + C[i++] = coeff; + } else if (skip == 16) { + k = 16; + if (i > 16) + k = 32 - i; + + while (k--) + C[i++] = 0; + } else { + k = skip; + if (skip > 31 - i) + k = 31 - i; + + while (k--) + C[i++] = 0; + + C[i++] = coeff; + } + } while (i <= 31); + + if (skip == -1) + while (i <= 31) + C[i++] = 0; + else + getACCoefficient(pIn, &coeff, cinfo, treeYAC); +} + +/* Same as huffmanDecoderY, except for the tables used */ +static void +huffmanDecoderUV(int *C, int *pIn, struct comp_info *cinfo) +{ + int coeff = 0; + int i = 1; + int k, skip; + + getDCCoefficient(pIn, C, cinfo, treeUVDC); + + i = 1; + do { + skip = getACCoefficient(pIn, &coeff, cinfo, treeUVAC); + + if (skip == -1) { + break; + } else if (skip == 0) { + C[i++] = coeff; + } else if (skip == 16) { + k = 16; + if (i > 16) + k = 32 - i; + + while (k--) + C[i++] = 0; + } else { + k = skip; + if (skip > 31 - i) + k = 31 - i; + + while (k--) + C[i++] = 0; + + C[i++] = coeff; + } + } while (i <= 31); + + if (skip == -1) + while (i <= 31) + C[i++] = 0; + else + getACCoefficient(pIn, &coeff, cinfo, treeUVAC); +} + +/****************************************************************************** + * iDCT Functions + ******************************************************************************/ + +#ifndef APPROXIMATE_MUL_BY_SHIFT + +#define IDCT_MESSAGE "iDCT with multiply" + +#define TIMES_16382(u) ((u) ? 16382 * (u) : 0) +#define TIMES_23168(u) ((u) ? 23168 * (u) : 0) +#define TIMES_30270(u) ((u) ? 30270 * (u) : 0) +#define TIMES_41986(u) ((u) ? 41986 * (u) : 0) +#define TIMES_35594(u) ((u) ? 35594 * (u) : 0) +#define TIMES_23783(u) ((u) ? 23783 * (u) : 0) +#define TIMES_8351(u) ((u) ? 8351 * (u) : 0) +#define TIMES_17391(u) ((u) ? 17391 * (u) : 0) +#define TIMES_14743(u) ((u) ? 14743 * (u) : 0) +#define TIMES_9851(u) ((u) ? 9851 * (u) : 0) +#define TIMES_3459(u) ((u) ? 3459 * (u) : 0) +#define TIMES_32134(u) ((u) ? 32134 * (u) : 0) +#define TIMES_27242(u) ((u) ? 27242 * (u) : 0) +#define TIMES_18202(u) ((u) ? 18202 * (u) : 0) +#define TIMES_6392(u) ((u) ? 6392 * (u) : 0) +#define TIMES_39550(u) ((u) ? 39550 * (u) : 0) +#define TIMES_6785(u) ((u) ? 6785 * (u) : 0) +#define TIMES_12538(u) ((u) ? 12538 * (u) : 0) + +#else + +#define IDCT_MESSAGE "iDCT with shift" + +#define TIMES_16382(u) ((u) ? x = (u), (x << 14) - (x << 1) : 0) +#define TIMES_23168(u) ((u) ? x = (u), (x << 14) + (x << 12) + (x << 11) + (x << 9) : 0) +#define TIMES_30270(u) ((u) ? x = (u), (x << 15) - (x << 11) : 0) +#define TIMES_41986(u) ((u) ? x = (u), (x << 15) + (x << 13) + (x << 10) : 0) +#define TIMES_35594(u) ((u) ? x = (u), (x << 15) + (x << 11) + (x << 9) + (x << 8) : 0) +#define TIMES_23783(u) ((u) ? x = (u), (x << 14) + (x << 13) - (x << 9) - (x << 8) : 0) +#define TIMES_8351(u) ((u) ? x = (u), (x << 13) : 0) +#define TIMES_17391(u) ((u) ? x = (u), (x << 14) + (x << 10) : 0) +#define TIMES_14743(u) ((u) ? x = (u), (x << 14) - (x << 10) - (x << 9) : 0) +#define TIMES_9851(u) ((u) ? x = (u), (x << 13) + (x << 10) + (x << 9) : 0) +#define TIMES_3459(u) ((u) ? x = (u), (x << 12) - (x << 9) : 0) +#define TIMES_32134(u) ((u) ? x = (u), (x << 15) - (x << 9) : 0) +#define TIMES_27242(u) ((u) ? x = (u), (x << 14) + (x << 13) + (x << 11) + (x << 9) : 0) +#define TIMES_18202(u) ((u) ? x = (u), (x << 14) + (x << 11) - (x << 8) : 0) +#define TIMES_6392(u) ((u) ? x = (u), (x << 13) - (x << 11) + (x << 8) : 0) +#define TIMES_39550(u) ((u) ? x = (u), (x << 15) + (x << 12) + (x << 11) + (x << 9) : 0) +#define TIMES_6785(u) ((u) ? x = (u), (x << 12) + (x << 11) + (x << 9) : 0) +#define TIMES_12538(u) ((u) ? x = (u), (x << 13) + (x << 12) + (x << 8) : 0) + +/* + * The variables C0, C4, C16 and C20 can also be removed from the algorithm + * if APPROXIMATE_MUL_BY_SHIFTS is defined. They store correction values + * and can be considered insignificant. + */ + +#endif + +static void +DCT_8x4(int *coeff, unsigned char *out) + /* pre: coeff == coefficients + post: coeff != coefficients + ** DO NOT ASSUME coeff TO BE THE SAME BEFORE AND AFTER CALLING THIS FUNCTION! + */ +{ + register int base, val1, val2, val3; + int tmp1, tmp2; + int C4, C16, C20; + int C2_18, C6_22, C1_17, C3_19, C5_21, C7_23; + register int t; +#ifdef APPROXIMATE_MUL_BY_SHIFT + register int x; +#endif + + C4 = coeff[4]; + C16 = coeff[16]; + C20 = coeff[20]; + + coeff[0] = TIMES_23168(coeff[0]); + coeff[4] = TIMES_23168(coeff[4]); + coeff[16] = TIMES_23168(coeff[16]); + coeff[20] = TIMES_23168(coeff[20]); + + C2_18 = coeff[2] + coeff[18]; + C6_22 = coeff[6] + coeff[22]; + C1_17 = coeff[1] + coeff[17]; + C3_19 = coeff[3] + coeff[19]; + C5_21 = coeff[5] + coeff[21]; + C7_23 = coeff[7] + coeff[23]; + + // 0,7,25,32 + + base = 0x1000000; + base += coeff[0] + coeff[4] + coeff[16] + coeff[20]; + base += TIMES_30270(C2_18); + base += TIMES_12538(C6_22); + + val1 = TIMES_41986(coeff[9]); + val1 += TIMES_35594(coeff[11]); + val1 += TIMES_23783(coeff[13]); + val1 += TIMES_8351(coeff[15]); + val1 += TIMES_17391(coeff[25]); + val1 += TIMES_14743(coeff[27]); + val1 += TIMES_9851(coeff[29]); + val1 += TIMES_3459(coeff[31]); + + val2 = TIMES_32134(C1_17); + val2 += TIMES_27242(C3_19); + val2 += TIMES_18202(C5_21); + val2 += TIMES_6392(C7_23); + + val3 = TIMES_39550(coeff[10]); + val3 += TIMES_16382(coeff[14] + coeff[26]); + val3 += TIMES_6785(coeff[30]); + val3 += TIMES_30270(coeff[8] + coeff[12]); + val3 += TIMES_12538(coeff[24] + coeff[28]); + + t = (base + val1 + val2 + val3) >> 17; + out[0] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3 - C4 - C20) >> 17; + out[7] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3 - C16 - C20) >> 17; + out[24] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3 - C4 - C16 - C20) >> 17; + out[31] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + //1,6,25,30 + + base = 0x1000000; + base += coeff[0] - coeff[4] + coeff[16] - coeff[20]; + base += TIMES_12538(C2_18); + base -= TIMES_30270(C6_22); + + val1 = TIMES_35594(coeff[9]); + val1 -= TIMES_8351(coeff[11]); + val1 -= TIMES_41986(coeff[13]); + val1 -= TIMES_23783(coeff[15]); + val1 -= TIMES_14743(coeff[25]); + val1 -= TIMES_3459(coeff[27]); + val1 -= TIMES_17391(coeff[29]); + val1 -= TIMES_9851(coeff[31]); + + val2 = TIMES_27242(C1_17); + val2 -= TIMES_6392(C3_19); + val2 -= TIMES_32134(C5_21); + val2 -= TIMES_18202(C7_23); + + val3 = TIMES_16382(coeff[10] - coeff[30]); + val3 -= TIMES_39550(coeff[14]); + val3 += TIMES_6785(coeff[26]); + val3 += TIMES_12538(coeff[24] - coeff[28]); + val3 += TIMES_30270(coeff[8] - coeff[12]); + + t = (base + val1 + val2 + val3 + C4 + C20) >> 17; + out[1] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3) >> 17; + out[6] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3 + C4 - C16 + C20) >> 17; + out[25] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3 + C20) >> 17; + out[30] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + //2,5,26,29 + + base = 0x1000000; + base += coeff[0] - coeff[4] + coeff[16] - coeff[20]; + base -= TIMES_12538(C2_18); + base += TIMES_30270(C6_22); + + val1 = TIMES_23783(coeff[9]); + val1 -= TIMES_41986(coeff[11]); + val1 += TIMES_8351(coeff[13]); + val1 += TIMES_35594(coeff[15]); + val1 += TIMES_9851(coeff[25]); + val1 -= TIMES_17391(coeff[27]); + val1 += TIMES_3459(coeff[29]); + val1 += TIMES_14743(coeff[31]); + + val2 = TIMES_18202(C1_17); + val2 -= TIMES_32134(C3_19); + val2 += TIMES_6392(C5_21); + val2 += TIMES_27242(C7_23); + + val3 = -TIMES_16382(coeff[10] - coeff[30]); + val3 += TIMES_39550(coeff[14]); + val3 -= TIMES_6785(coeff[26]); + val3 += TIMES_12538(coeff[24] - coeff[28]); + val3 += TIMES_30270(coeff[8] - coeff[12]); + + t = (base + val1 + val2 + val3) >> 17; + out[2] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3) >> 17; + out[5] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3 - C16) >> 17; + out[26] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3 + C4 - C16 + C20) >> 17; + out[29] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + //3,4,27,28 + + base = 0x1000000; + base += coeff[0] + coeff[4] + coeff[16] + coeff[20]; + base -= TIMES_30270(C2_18); + base -= TIMES_12538(C6_22); + + val1 = TIMES_8351(coeff[9]); + val1 -= TIMES_23783(coeff[11]); + val1 += TIMES_35594(coeff[13]); + val1 += TIMES_3459(coeff[25]); + val1 -= TIMES_9851(coeff[27]); + val1 += TIMES_14743(coeff[29]); + + val2 = TIMES_6392(C1_17); + val2 -= TIMES_18202(C3_19); + val2 += TIMES_27242(C5_21); + + val3 = -TIMES_39550(coeff[10]); + val3 += TIMES_16382(coeff[14] + coeff[26]); + val3 -= TIMES_6785(coeff[30]); + val3 += TIMES_30270(coeff[8] + coeff[12]); + val3 += TIMES_12538(coeff[24] + coeff[28]); + + tmp1 = TIMES_32134(C7_23); + tmp2 = TIMES_41986(coeff[15]) + TIMES_17391(coeff[31]); + + t = (base + val1 + val2 + val3 - tmp1 - tmp2 - C4 - C20) >> 17; + out[3] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3) >> 17; + out[4] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3 - tmp1 + tmp2) >> 17; + out[27] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3 - C16 - C20) >> 17; + out[28] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + // Second half + C2_18 = coeff[2] - coeff[18]; + C6_22 = coeff[6] - coeff[22]; + C1_17 = coeff[1] - coeff[17]; + C3_19 = coeff[3] - coeff[19]; + C5_21 = coeff[5] - coeff[21]; + C7_23 = coeff[7] - coeff[23]; + + // 8,15,16,23 + + base = 0x1000000; + base += coeff[0] + coeff[4] - coeff[16] - coeff[20]; + base += TIMES_30270(C2_18); + base += TIMES_12538(C6_22); + + val1 = TIMES_17391(coeff[9]); + val1 += TIMES_14743(coeff[11]); + val1 += TIMES_9851(coeff[13]); + val1 += TIMES_3459(coeff[15]); + val1 -= TIMES_41986(coeff[25]); + val1 -= TIMES_35594(coeff[27]); + val1 -= TIMES_23783(coeff[29]); + val1 -= TIMES_8351(coeff[31]); + + val2 = TIMES_32134(C1_17); + val2 += TIMES_27242(C3_19); + val2 += TIMES_18202(C5_21); + val2 += TIMES_6392(C7_23); + + val3 = TIMES_16382(coeff[10] - coeff[30]); + val3 += TIMES_6785(coeff[14]); + val3 -= TIMES_39550(coeff[26]); + val3 -= TIMES_30270(coeff[24] + coeff[28]); + val3 += TIMES_12538(coeff[8] + coeff[12]); + + t = (base + val1 + val2 + val3) >> 17; + out[8] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3 - C4 + C16 + C20) >> 17; + out[15] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3) >> 17; + out[16] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3 - C4 + C20) >> 17; + out[23] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + //9,14,17,22 + + base = 0x1000000; + base += coeff[0] - coeff[4] - coeff[16] + coeff[20]; + base += TIMES_12538(C2_18); + base -= TIMES_30270(C6_22); + + val1 = TIMES_14743(coeff[9]); + val1 -= TIMES_3459(coeff[11]); + val1 -= TIMES_17391(coeff[13]); + val1 -= TIMES_9851(coeff[15]); + val1 -= TIMES_35594(coeff[25]); + val1 += TIMES_8351(coeff[27]); + val1 += TIMES_41986(coeff[29]); + val1 += TIMES_23783(coeff[31]); + + val2 = TIMES_27242(C1_17); + val2 -= TIMES_6392(C3_19); + val2 -= TIMES_32134(C5_21); + val2 -= TIMES_18202(C7_23); + + val3 = TIMES_6785(coeff[10]); + val3 -= TIMES_16382(coeff[14] + coeff[26]); + val3 += TIMES_39550(coeff[30]); + val3 += TIMES_12538(coeff[8] - coeff[12]); + val3 -= TIMES_30270(coeff[24] - coeff[28]); + + t = (base + val1 + val2 + val3 + C4 + C16 - C20) >> 17; + out[9] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3 + C16) >> 17; + out[14] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3 + C4) >> 17; + out[17] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3) >> 17; + out[22] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + //10,13,18,21 + + base = 0x1000000; + base += coeff[0] - coeff[4] - coeff[16] + coeff[20]; + base -= TIMES_12538(C2_18); + base += TIMES_30270(C6_22); + + val1 = TIMES_9851(coeff[9]); + val1 -= TIMES_17391(coeff[11]); + val1 += TIMES_3459(coeff[13]); + val1 += TIMES_14743(coeff[15]); + val1 -= TIMES_23783(coeff[25]); + val1 += TIMES_41986(coeff[27]); + val1 -= TIMES_8351(coeff[29]); + val1 -= TIMES_35594(coeff[31]); + + val2 = TIMES_18202(C1_17); + val2 -= TIMES_32134(C3_19); + val2 += TIMES_6392(C5_21); + val2 += TIMES_27242(C7_23); + + val3 = -TIMES_6785(coeff[10]); + val3 += TIMES_16382(coeff[14] + coeff[26]); + val3 -= TIMES_39550(coeff[30]); + val3 += TIMES_12538(coeff[8] - coeff[12]); + val3 -= TIMES_30270(coeff[24] - coeff[28]); + + t = (base + val1 + val2 + val3) >> 17; + out[10] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3 + C4 + C16 - C20) >> 17; + out[13] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3) >> 17; + out[18] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3 + C4) >> 17; + out[21] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + + // 11,12,19,20 + + base = 0x1000000; + base += coeff[0] + coeff[4] - coeff[16] - coeff[20]; + base -= TIMES_30270(C2_18); + base -= TIMES_12538(C6_22); + + val1 = TIMES_3459(coeff[9]); + val1 -= TIMES_9851(coeff[11]); + val1 += TIMES_14743(coeff[13]); + val1 -= TIMES_8351(coeff[25]); + val1 += TIMES_23783(coeff[27]); + val1 -= TIMES_35594(coeff[29]); + + val2 = TIMES_6392(C1_17); + val2 -= TIMES_18202(C3_19); + val2 += TIMES_27242(C5_21); + + val3 = -TIMES_16382(coeff[10] - coeff[30]); + val3 -= TIMES_6785(coeff[14]); + val3 += TIMES_39550(coeff[26]); + val3 -= TIMES_30270(coeff[24] + coeff[28]); + val3 += TIMES_12538(coeff[8] + coeff[12]); + + tmp1 = TIMES_32134(C7_23); + tmp2 = -TIMES_17391(coeff[15]) + TIMES_41986(coeff[31]); + + t = (base + val1 + val2 + val3 - tmp1 + tmp2 + C16 + C20) >> 17; + out[11] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 - val2 + val3 + C16 + C20) >> 17; + out[12] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base - val1 + val2 - val3 - tmp1 - tmp2 - C4 + C20) >> 17; + out[19] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; + t = (base + val1 - val2 - val3) >> 17; + out[20] = t & 0xFFFFFF00 ? t < 0 ? 0 : 255 : (unsigned char)t; +} + +#undef TIMES_16382 +#undef TIMES_23168 +#undef TIMES_30270 +#undef TIMES_41986 +#undef TIMES_35594 +#undef TIMES_23783 +#undef TIMES_8351 +#undef TIMES_17391 +#undef TIMES_14743 +#undef TIMES_9851 +#undef TIMES_3459 +#undef TIMES_32134 +#undef TIMES_27242 +#undef TIMES_18202 +#undef TIMES_6392 +#undef TIMES_39550 +#undef TIMES_6785 +#undef TIMES_12538 + +/****************************************************************************** + * Main Decoder Functions + ******************************************************************************/ + +/* This function handles the decompression of a single 8x4 block. It is + * independent of the palette (YUV422, YUV420, YUV400, GBR422...). cinfo->bytes + * determines the positin in the input buffer. + */ +static int +decompress8x4(unsigned char *pOut, + unsigned char *pIn, + int *lastDC, + int uvFlag, + struct comp_info *cinfo) +{ + int i, x, y, dc; + int coeffs[32]; + int deZigZag[32]; + int *dest; + int *src; + unsigned char *qt = cinfo->qt; + + if (!uvFlag) { + huffmanDecoderY(coeffs, (int *)pIn, cinfo); + + /* iDPCM and dequantize first coefficient */ + dc = (*lastDC) + coeffs[0]; + coeffs[0] = dc * (qt[0] + 1); + *lastDC = dc; + + /* ...and the second coefficient */ + coeffs[1] = ((qt[1] + 1) * coeffs[1]) >> 1; + + /* Dequantize, starting at 3rd element */ + for (i = 2; i < 32; i++) + coeffs[i] = (qt[i] + 1) * coeffs[i]; + } else { + huffmanDecoderUV(coeffs, (int *)pIn, cinfo); + + /* iDPCM */ + dc = (*lastDC) + coeffs[0]; + coeffs[0] = dc; + *lastDC = dc; + + /* Dequantize */ + for (i = 0; i < 32; i++) + coeffs[i] = (qt[32 + i] + 1) * coeffs[i]; + } + + /* Dezigzag */ + for (i = 0; i < 32; i++) + deZigZag[i] = coeffs[ZigZag518[i]]; + + /* Transpose the dezigzagged coefficient matrix */ + src = deZigZag; + dest = coeffs; + for (y = 0; y <= 3; ++y) { + for (x = 0; x <= 7; ++x) + dest[x] = src[x * 4]; + src += 1; + dest += 8; + } + + /* Do the inverse DCT transform */ + DCT_8x4(coeffs, pOut); + + return 0; /* Always returns 0 */ +} + +static inline void +copyBlock(unsigned char *src, unsigned char *dest, int destInc) +{ + int i; + unsigned int *pSrc, *pDest; + + for (i = 0; i <= 3; i++) { + pSrc = (unsigned int *) src; + pDest = (unsigned int *) dest; + pDest[0] = pSrc[0]; + pDest[1] = pSrc[1]; + src += 8; + dest += destInc; + } +} + +#if 0 +static inline int +decompress400NoMMXOV518(unsigned char *pIn, + unsigned char *pOut, + unsigned char *pTmp, + const int w, + const int h, + const int numpix, + struct comp_info *cinfo) +{ + int iOutY, x, y; + int lastYDC = 0; + + /* Start Y loop */ + y = 0; + do { + iOutY = w * y; + x = 0; + do { + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + x += 8; + } while (x < w); + y += 4; + } while (y < h); + + /* Did we decode too much? */ + if (cinfo->bytes > cinfo->rawLen + 897) + return 1; + + /* Did we decode enough? */ + if (cinfo->bytes >= cinfo->rawLen - 897) + return 0; + else + return 1; +} +#endif + +static inline int +decompress420NoMMXOV518(unsigned char *pIn, + unsigned char *pOut, + unsigned char *pTmp, + const int w, + const int h, + const int numpix, + struct comp_info *cinfo, + int yvu) +{ + unsigned char *pOutU, *pOutV; + int iOutY, iOutU, iOutV, x, y; + int lastYDC = 0; + int lastUDC = 0; + int lastVDC = 0; + + if (yvu) { + pOutV = pOut + numpix; + pOutU = pOutV + numpix / 4; + } else { + pOutU = pOut + numpix; + pOutV = pOutU + numpix / 4; + } + + /* Start Y loop */ + y = 0; + do { + iOutY = w * y; + iOutV = iOutU = iOutY / 4; + + x = 0; + do { + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + x += 8; + } while (x < w); + + + + iOutY = w * (y + 4); + x = 0; + do { + decompress8x4(pTmp, pIn, &lastUDC, 1, cinfo); + copyBlock(pTmp, pOutU + iOutU, w/2); + iOutU += 8; + + decompress8x4(pTmp, pIn, &lastVDC, 1, cinfo); + copyBlock(pTmp, pOutV + iOutV, w/2); + iOutV += 8; + + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + + decompress8x4(pTmp, pIn, &lastYDC, 0, cinfo); + copyBlock(pTmp, pOut + iOutY, w); + iOutY += 8; + + x += 16; + } while (x < w); + + y += 8; + } while (y < h); + + /* Did we decode too much? */ + if (cinfo->bytes > cinfo->rawLen + 897) + return 1; + + /* Did we decode enough? */ + if (cinfo->bytes >= cinfo->rawLen - (897 + 64)) + return 0; + else + return 1; +} + +/* Get quantization tables from input + * Returns: <0 if error, or >=0 otherwise */ +static int +get_qt_dynamic(unsigned char *pIn, struct comp_info *cinfo) +{ + int rawLen = cinfo->rawLen; + + /* Make sure input is actually big enough to hold trailer */ + if (rawLen < 72) + return -1; + + cinfo->qt = pIn + rawLen - 64; + + return 0; +} + +/* Remove all 0 blocks from input */ +static void remove0blocks(unsigned char *pIn, int *inSize) +{ + long long *in = (long long *)pIn; + long long *out = (long long *)pIn; + int i; + + for (i = 0; i < *inSize; i += 8, in++) + /* Skip 8 byte blocks of all 0 */ + if (*in) + *out++ = *in; + + *inSize -= (in - out) * 8; +} + +#if 0 /* not used */ +/* Input format is raw isoc. data (with intact SOF header, packet numbers + * stripped, and all-zero blocks removed). + * Output format is planar YUV400 + * Returns uncompressed data length if success, or zero if error + */ +static int +Decompress400(unsigned char *pIn, + unsigned char *pOut, + int w, + int h, + int inSize) +{ + struct comp_info cinfo; + int numpix = w * h; + unsigned char pTmp[32]; + + remove0blocks(pIn, &inSize); + + cinfo.bytes = 0; + cinfo.bits = 0; + cinfo.rawLen = inSize; + + if (get_qt_dynamic(pIn, &cinfo) < 0) + return 0; + + /* Decompress, skipping the 8-byte SOF header */ + if (decompress400NoMMXOV518(pIn + 8, pOut, pTmp, w, h, numpix, &cinfo)) + /* return 0; */ + ; /* Don't return error yet */ + + return numpix; +} +#endif + +/* Input format is raw isoc. data (with intact SOF header, packet numbers + * stripped, and all-zero blocks removed). + * Output format is planar YUV420 + * Returns uncompressed data length if success, or zero if error + */ +static int v4lconvert_ov518_to_yuv420(unsigned char *src, unsigned char *dst, + int w, int h, int yvu, int inSize) +{ + struct comp_info cinfo; + int numpix = w * h; + unsigned char pTmp[32]; + + remove0blocks(src, &inSize); + + cinfo.bytes = 0; + cinfo.bits = 0; + cinfo.rawLen = inSize; + + if (get_qt_dynamic(src, &cinfo) < 0) + return -1; + + /* Decompress, skipping the 8-byte SOF header */ + if (decompress420NoMMXOV518(src + 8, dst, pTmp, w, h, numpix, &cinfo, yvu)) + return -1; + + return 0; +} + +int main(int argc, char *argv[]) +{ + int width, height, yvu, src_size, dest_size; + unsigned char src_buf[200000]; + unsigned char dest_buf[500000]; + + while (1) { + if (v4lconvert_helper_read(STDIN_FILENO, &width, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &height, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &yvu, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (v4lconvert_helper_read(STDIN_FILENO, &src_size, sizeof(int), argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (src_size > sizeof(src_buf)) { + fprintf(stderr, "%s: error: src_buf too small, need: %d\n", + argv[0], src_size); + return 2; + } + + if (v4lconvert_helper_read(STDIN_FILENO, src_buf, src_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + + dest_size = width * height * 3 / 2; + if (width <= 0 || width > SHRT_MAX || height <= 0 || height > SHRT_MAX) { + fprintf(stderr, "%s: error: width or height out of bounds\n", + argv[0]); + dest_size = -1; + } else if (dest_size > sizeof(dest_buf)) { + fprintf(stderr, "%s: error: dest_buf too small, need: %d\n", + argv[0], dest_size); + dest_size = -1; + } else if (v4lconvert_ov518_to_yuv420(src_buf, dest_buf, width, height, + yvu, src_size)) + dest_size = -1; + + if (v4lconvert_helper_write(STDOUT_FILENO, &dest_size, sizeof(int), + argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + + if (dest_size == -1) + continue; + + if (v4lconvert_helper_write(STDOUT_FILENO, dest_buf, dest_size, argv[0])) + return 1; /* Erm, no way to recover without loosing sync with libv4l */ + } +} diff --git a/libv4lconvert/pac207.c b/libv4lconvert/pac207.c new file mode 100644 index 0000000..ee9650a --- /dev/null +++ b/libv4lconvert/pac207.c @@ -0,0 +1,436 @@ +/* + +# PAC207 decoder +# Bertrik.Sikken. Thomas Kaiser (C) 2005 +# Copyright (C) 2003 2004 2005 Michel Xhaard + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + +# Note this code was originally licensed under the GNU GPL instead of the +# GNU LGPL, its license has been changed with permission, see the permission +# mails at the end of this file. + + */ + +#include +#include "libv4lconvert-priv.h" + +#define CLIP(color) (unsigned char)(((color) > 0xFF) ? 0xff : (((color) < 0) ? 0 : (color))) + +/* FIXME not threadsafe */ +static int decoder_initialized; + +static struct { + unsigned char is_abs; + unsigned char len; + signed char val; +} table[256]; + +static void init_pixart_decoder(void) +{ + int i; + int is_abs, val, len; + + for (i = 0; i < 256; i++) { + is_abs = 0; + val = 0; + len = 0; + if ((i & 0xC0) == 0) { + /* code 00 */ + val = 0; + len = 2; + } else if ((i & 0xC0) == 0x40) { + /* code 01 */ + val = -1; + len = 2; + } else if ((i & 0xC0) == 0x80) { + /* code 10 */ + val = 1; + len = 2; + } else if ((i & 0xF0) == 0xC0) { + /* code 1100 */ + val = -2; + len = 4; + } else if ((i & 0xF0) == 0xD0) { + /* code 1101 */ + val = 2; + len = 4; + } else if ((i & 0xF8) == 0xE0) { + /* code 11100 */ + val = -3; + len = 5; + } else if ((i & 0xF8) == 0xE8) { + /* code 11101 */ + val = 3; + len = 5; + } else if ((i & 0xFC) == 0xF0) { + /* code 111100 */ + val = -4; + len = 6; + } else if ((i & 0xFC) == 0xF4) { + /* code 111101 */ + val = 4; + len = 6; + } else if ((i & 0xF8) == 0xF8) { + /* code 11111xxxxxx */ + is_abs = 1; + val = 0; + len = 5; + } + table[i].is_abs = is_abs; + table[i].val = val; + table[i].len = len; + } + decoder_initialized = 1; +} + +static inline unsigned char getByte(const unsigned char *inp, + unsigned int bitpos) +{ + const unsigned char *addr; + + addr = inp + (bitpos >> 3); + return (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); +} + +static inline unsigned short getShort(const unsigned char *pt) +{ + return ((pt[0] << 8) | pt[1]); +} + +static int +pac_decompress_row(const unsigned char *inp, unsigned char *outp, int width, + int step_size, int abs_bits) +{ + int col; + int val; + int bitpos; + unsigned char code; + + if (!decoder_initialized) + init_pixart_decoder(); + + /* first two pixels are stored as raw 8-bit */ + *outp++ = inp[2]; + *outp++ = inp[3]; + bitpos = 32; + + /* main decoding loop */ + for (col = 2; col < width; col++) { + /* get bitcode */ + + code = getByte(inp, bitpos); + bitpos += table[code].len; + + /* calculate pixel value */ + if (table[code].is_abs) { + /* absolute value: get 6 more bits */ + code = getByte(inp, bitpos); + bitpos += abs_bits; + *outp++ = code & ~(0xff >> abs_bits); + } else { + /* relative to left pixel */ + val = outp[-2] + table[code].val * step_size; + *outp++ = CLIP(val); + } + } + + /* return line length, rounded up to next 16-bit word */ + return 2 * ((bitpos + 15) / 16); +} + +int v4lconvert_decode_pac207(struct v4lconvert_data *data, + const unsigned char *inp, int src_size, unsigned char *outp, + int width, int height) +{ + /* we should received a whole frame with header and EOL marker + in myframe->data and return a GBRG pattern in frame->tmpbuffer + remove the header then copy line by line EOL is set with 0x0f 0xf0 marker + or 0x1e 0xe1 for compressed line*/ + const unsigned char *end = inp + src_size; + unsigned short word; + int row; + + /* iterate over all rows */ + for (row = 0; row < height; row++) { + if ((inp + 2) > end) { + V4LCONVERT_ERR("incomplete pac207 frame\n"); + return -1; + } + word = getShort(inp); + switch (word) { + case 0x0FF0: + memcpy(outp, inp + 2, width); + inp += (2 + width); + break; + case 0x1EE1: + inp += pac_decompress_row(inp, outp, width, 5, 6); + break; + + case 0x2DD2: + inp += pac_decompress_row(inp, outp, width, 9, 5); + break; + + case 0x3CC3: + inp += pac_decompress_row(inp, outp, width, 17, 4); + break; + + case 0x4BB4: + /* skip or copy line? */ + memcpy(outp, outp - 2 * width, width); + inp += 2; + break; + + default: /* corrupt frame */ + V4LCONVERT_ERR("unknown pac207 row header: 0x%04x\n", (int)word); + return -1; + } + outp += width; + } + + return 0; +} + + + + +/* + Return-Path: +Received: from koko.hhs.nl ([145.52.2.16] verified) +by hhs.nl (CommuniGate Pro SMTP 4.3.6) +with ESMTP id 88906346 for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 01:17:00 +0200 +Received: from exim (helo=koko) +by koko.hhs.nl with local-smtp (Exim 4.62) +(envelope-from ) +id 1KBeEW-0001qu-H6 +for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 01:17:00 +0200 +Received: from [192.87.102.74] (port=41049 helo=filter6-ams.mf.surf.net) +by koko.hhs.nl with esmtp (Exim 4.62) +(envelope-from ) +id 1KBeEV-0001qn-2T +for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 01:17:00 +0200 +Received: from smtp0.lie-comtel.li (smtp0.lie-comtel.li [217.173.238.80]) +by filter6-ams.mf.surf.net (8.13.8/8.13.8/Debian-3) with ESMTP id m5PNGwSF007539 +for ; Thu, 26 Jun 2008 01:16:58 +0200 +Received: from localhost (localhost.lie-comtel.li [127.0.0.1]) +by smtp0.lie-comtel.li (Postfix) with ESMTP id DDB609FEC1D; +Thu, 26 Jun 2008 00:16:56 +0100 (GMT-1) +X-Virus-Scanned: Virus scanned by amavis at smtp.lie-comtel.li +Received: from [192.168.0.16] (217-173-228-198.cmts.powersurf.li [217.173.228.198]) +by smtp0.lie-comtel.li (Postfix) with ESMTP id 80B589FEC19; +Thu, 26 Jun 2008 00:16:56 +0100 (GMT-1) +Message-ID: <4862D211.3000802@kaiser-linux.li> +Date: Thu, 26 Jun 2008 01:17:37 +0200 +From: Thomas Kaiser +User-Agent: Thunderbird 2.0.0.14 (X11/20080505) +MIME-Version: 1.0 +To: Hans de Goede +CC: Thomas Kaiser , bertrik@zonnet.nl, +mxhaard@magic.fr +Subject: Re: pac207 bayer decompression algorithm license question +References: <4862C0A4.3060003@hhs.nl> +In-Reply-To: <4862C0A4.3060003@hhs.nl> +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 7bit +X-Canit-CHI2: 0.00 +X-Bayes-Prob: 0.0001 (Score 0, tokens from: @@RPTN) +X-Spam-Score: 0.00 () [Tag at 8.00] +X-CanItPRO-Stream: hhs:j.w.r.degoede@hhs.nl (inherits from hhs:default,base:default) +X-Canit-Stats-ID: 88604132 - 38b3b44cd798 +X-Scanned-By: CanIt (www . roaringpenguin . com) on 192.87.102.74 +X-Anti-Virus: Kaspersky Anti-Virus for MailServers 5.5.2/RELEASE, bases: 25062008 #787666, status: clean + +Hello Hans + +Hans de Goede wrote: +> Hi, +> +> As you may have seen on the mailinglist, I've created a userspace +> library to handle cam specific format handling in userspace where it +> belongs, see: +> http://hansdegoede.livejournal.com/ +Yes, I saw it on the mail list and I think it is a good idea :-) +> +> I would like to also add support for decompressing the pac207's +> compressed bayer to this lib (and remove it from the kernel driver) +> for this I need permission to relicense the decompress code under the +> LGPL (version 2 or later). +Actually, this was done by Bertrik Sikken (bertrik@zonnet.nl), Michel +Xhaard (mxhaard@magic.fr) and me. But Bertrik was the one who found out +how to decode the lines :-) +> +> Can you give me permission for this, or if the code is not yours put +> me in contact with someone who can? +For me it's no problem to release it with LGPL. Maybe you have to ask +the other one's also. +> +> Thanks & Regards, +> +> Hans + +Rgeards, Thomas +*/ + +/* + Return-Path: + Received: from koko.hhs.nl ([145.52.2.16] verified) + by hhs.nl (CommuniGate Pro SMTP 4.3.6) + with ESMTP id 88910192 for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 09:15:37 +0200 + Received: from exim (helo=koko) + by koko.hhs.nl with local-smtp (Exim 4.62) + (envelope-from ) + id 1KBlhh-0006Fi-Oe + for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 09:15:37 +0200 + Received: from [194.171.167.220] (port=54180 helo=filter4-til.mf.surf.net) + by koko.hhs.nl with esmtp (Exim 4.62) + (envelope-from ) + id 1KBlhh-0006Fd-FY + for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 09:15:37 +0200 + Received: from smtp4-g19.free.fr (smtp4-g19.free.fr [212.27.42.30]) + by filter4-til.mf.surf.net (8.13.8/8.13.8/Debian-3) with ESMTP id m5Q7FY1I006360 + for ; Thu, 26 Jun 2008 09:15:34 +0200 + Received: from smtp4-g19.free.fr (localhost.localdomain [127.0.0.1]) + by smtp4-g19.free.fr (Postfix) with ESMTP id 51C683EA0E7; + Thu, 26 Jun 2008 09:15:34 +0200 (CEST) + Received: from [192.168.1.11] (lns-bzn-54-82-251-105-53.adsl.proxad.net [82.251.105.53]) + by smtp4-g19.free.fr (Postfix) with ESMTP id 1149E3EA0C7; + Thu, 26 Jun 2008 09:15:34 +0200 (CEST) + From: Michel Xhaard + To: Hans de Goede + Subject: Re: pac207 bayer decompression algorithm license question + Date: Thu, 26 Jun 2008 11:15:32 +0200 + User-Agent: KMail/1.9.5 + Cc: bertrik@zonnet.nl, spca5xx@kaiser-linux.li, + "Jean-Francois Moine" + References: <48633F02.3040108@hhs.nl> + In-Reply-To: <48633F02.3040108@hhs.nl> + MIME-Version: 1.0 + Content-Type: text/plain; + charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + Content-Disposition: inline + Message-Id: <200806261115.32909.mxhaard@magic.fr> + X-Canit-CHI2: 0.00 + X-Bayes-Prob: 0.0001 (Score 0, tokens from: @@RPTN) + X-Spam-Score: 0.00 () [Tag at 8.00] + X-CanItPRO-Stream: hhs:j.w.r.degoede@hhs.nl (inherits from hhs:default,base:default) + X-Canit-Stats-ID: 88656338 - 0dde233cb8b5 + X-Scanned-By: CanIt (www . roaringpenguin . com) on 194.171.167.220 + X-Anti-Virus: Kaspersky Anti-Virus for MailServers 5.5.2/RELEASE, bases: 26062008 #787720, status: clean + + Le jeudi 26 juin 2008 09:02, Hans de Goede a =E9crit=A0: + > Hi, + > + > As you may have seen on the mailinglist, I've created a userspace library + > to handle cam specific format handling in userspace, see: + > http://hansdegoede.livejournal.com/ + > + > I would like to also add support for decompressing the pac207's compressed + > bayer to this lib (and remove it from the kernel driver) and I've heard + > from Thomas Kaiser that you are a co-author of the decompression code. In + > order to add support for decompressing pac207 compressed bayer to libv4l I + > need permission to relicense the decompression code under the LGPL (versi= + on + > 2 or later). + > + > Can you give me permission for this? + > + > Thanks & Regards, + > + > Hans + > + > + > + > p.s. +> +> Thomas has already given permission. + +=46or me it is ok and a good idea for all free world familly ;-). +Bests regards +=2D-=20 +Michel Xhaard +http://mxhaard.free.fr +*/ + +/* + Return-Path: + Received: from koko.hhs.nl ([145.52.2.16] verified) + by hhs.nl (CommuniGate Pro SMTP 4.3.6) + with ESMTP id 88940205 for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 22:03:30 +0200 + Received: from exim (helo=koko) + by koko.hhs.nl with local-smtp (Exim 4.62) + (envelope-from ) + id 1KBxgo-0003Dj-ET + for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 22:03:30 +0200 + Received: from [192.87.102.69] (port=51992 helo=filter1-ams.mf.surf.net) + by koko.hhs.nl with esmtp (Exim 4.62) + (envelope-from ) + id 1KBxgo-0003Dd-5i + for j.w.r.degoede@hhs.nl; Thu, 26 Jun 2008 22:03:30 +0200 + Received: from pelian.kabelfoon.nl (pelian3.kabelfoon.nl [62.45.45.106]) + by filter1-ams.mf.surf.net (8.13.8/8.13.8/Debian-3) with ESMTP id m5QK3ThE007720 + for ; Thu, 26 Jun 2008 22:03:29 +0200 + Received: from [192.168.1.1] (062-015-045-062.dynamic.caiway.nl [62.45.15.62]) + by pelian.kabelfoon.nl (Postfix) with ESMTP id 9239B428100 + for ; Thu, 26 Jun 2008 22:03:29 +0200 (CEST) + Message-ID: <4863F611.80104@sikken.nl> + Date: Thu, 26 Jun 2008 22:03:29 +0200 + From: Bertrik Sikken + User-Agent: Thunderbird 2.0.0.14 (Windows/20080421) + MIME-Version: 1.0 + To: Hans de Goede + Subject: Re: pac207 bayer decompression algorithm license question + References: <48633F02.3040108@hhs.nl> + In-Reply-To: <48633F02.3040108@hhs.nl> + X-Enigmail-Version: 0.95.6 + Content-Type: text/plain; charset=ISO-8859-1; format=flowed + Content-Transfer-Encoding: 7bit + X-Canit-CHI2: 0.00 + X-Bayes-Prob: 0.0001 (Score 0, tokens from: @@RPTN) + X-Spam-Score: 0.00 () [Tag at 8.00] + X-CanItPRO-Stream: hhs:j.w.r.degoede@hhs.nl (inherits from hhs:default,base:default) + X-Canit-Stats-ID: 88938005 - ef1f0836ffc7 + X-Scanned-By: CanIt (www . roaringpenguin . com) on 192.87.102.69 + X-Anti-Virus: Kaspersky Anti-Virus for MailServers 5.5.2/RELEASE, bases: 26062008 #787877, status: clean + + Hallo Hans, + + Hans de Goede wrote: + > Hi, + > + > As you may have seen on the mailinglist, I've created a userspace + > library to + > handle cam specific format handling in userspace, see: + > http://hansdegoede.livejournal.com/ + + O leuk, zoiets is naar mijn idee precies wat er nodig is voor webcam + support onder linux. Ik ben een jaar of 3 geleden heel actief geweest + met een aantal webcams, maar doe er tegenwoordig helemaal niets meer + aan. + + > I would like to also add support for decompressing the pac207's compressed + > bayer to this lib (and remove it from the kernel driver) and I've heard + > from Thomas Kaiser that you are a co-author of the decompression code. + > In order to add support for decompressing pac207 compressed bayer to + > libv4l I need + > permission to relicense the decompression code under the LGPL (version 2 + > or later). + > + > Can you give me permission for this? + + Ja, vind ik goed. + + Vriendelijke groet, + Bertrik + */ diff --git a/libv4lconvert/processing/autogain.c b/libv4lconvert/processing/autogain.c new file mode 100644 index 0000000..c24eb71 --- /dev/null +++ b/libv4lconvert/processing/autogain.c @@ -0,0 +1,217 @@ +/* +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include + +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ +#include "../libv4lsyscall-priv.h" + +static int autogain_active(struct v4lprocessing_data *data) +{ + int autogain; + + autogain = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN); + if (!autogain) { + /* Reset last_correction val */ + data->last_gain_correction = 0; + } + + return autogain; +} + +/* Adjust ctrl value with steps steps, while not crossing limit */ +static void autogain_adjust(struct v4l2_queryctrl *ctrl, int *value, + int steps, int limit, int accel) +{ + int ctrl_range = (ctrl->maximum - ctrl->minimum) / ctrl->step; + + /* If we are of 3 * deadzone or more, and we have a fine grained + control, take larger steps, otherwise we take ages to get to the + right setting point. We use 256 as tripping point for determining + fine grained controls here, as avg_lum has a range of 0 - 255. */ + if (accel && abs(steps) >= 3 && ctrl_range > 256) + *value += steps * ctrl->step * (ctrl_range / 256); + /* If we are of by less then 3, but have a very finegrained control + still speed things up a bit */ + else if (accel && ctrl_range > 1024) + *value += steps * ctrl->step * (ctrl_range / 1024); + else + *value += steps * ctrl->step; + + if (steps > 0) { + if (*value > limit) + *value = limit; + } else { + if (*value < limit) + *value = limit; + } +} + +/* auto gain and exposure algorithm based on the knee algorithm described here: +http://ytse.tricolour.net/docs/LowLightOptimization.html */ +static int autogain_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y, target, steps, avg_lum = 0; + int gain, exposure, orig_gain, orig_exposure, exposure_low; + struct v4l2_control ctrl; + struct v4l2_queryctrl gainctrl, expoctrl; + const int deadzone = 6; + + ctrl.id = V4L2_CID_EXPOSURE; + expoctrl.id = V4L2_CID_EXPOSURE; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &expoctrl) || + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) + return 0; + + exposure = orig_exposure = ctrl.value; + /* Determine a value below which we try to not lower the exposure, + as most exposure controls tend to jump with big steps in the low + range, causing oscilation, so we prefer to use gain when exposure + has hit this value */ + exposure_low = (expoctrl.maximum - expoctrl.minimum) / 10; + /* If we have a fine grained exposure control only avoid the last 10 steps */ + steps = exposure_low / expoctrl.step; + if (steps > 10) + steps = 10; + exposure_low = steps * expoctrl.step + expoctrl.minimum; + + ctrl.id = V4L2_CID_GAIN; + gainctrl.id = V4L2_CID_GAIN; + if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &gainctrl) || + SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) + return 0; + gain = orig_gain = ctrl.value; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: + buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + + fmt->fmt.pix.width / 4; + + for (y = 0; (unsigned)y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width / 2; x++) + avg_lum += *buf++; + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2; + } + avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4; + break; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + + fmt->fmt.pix.width * 3 / 4; + + for (y = 0; (unsigned)y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width / 2; x++) { + avg_lum += *buf++; + avg_lum += *buf++; + avg_lum += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2; + } + avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4; + break; + } + + /* If we are off a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET); + steps = (target - avg_lum) / deadzone; + + /* If we were decreasing and are now increasing, or vica versa, half the + number of steps to avoid overshooting and oscilating */ + if ((steps > 0 && data->last_gain_correction < 0) || + (steps < 0 && data->last_gain_correction > 0)) + steps /= 2; + + if (steps == 0) + return 0; /* Nothing to do */ + + if (steps < 0) { + if (exposure > expoctrl.default_value) + autogain_adjust(&expoctrl, &exposure, steps, + expoctrl.default_value, 1); + else if (gain > gainctrl.default_value) + autogain_adjust(&gainctrl, &gain, steps, + gainctrl.default_value, 1); + else if (exposure > exposure_low) + autogain_adjust(&expoctrl, &exposure, steps, + exposure_low, 1); + else if (gain > gainctrl.minimum) + autogain_adjust(&gainctrl, &gain, steps, + gainctrl.minimum, 1); + else if (exposure > expoctrl.minimum) + autogain_adjust(&expoctrl, &exposure, steps, + expoctrl.minimum, 0); + else + steps = 0; + } else { + if (exposure < exposure_low) + autogain_adjust(&expoctrl, &exposure, steps, + exposure_low, 0); + else if (gain < gainctrl.default_value) + autogain_adjust(&gainctrl, &gain, steps, + gainctrl.default_value, 1); + else if (exposure < expoctrl.default_value) + autogain_adjust(&expoctrl, &exposure, steps, + expoctrl.default_value, 1); + else if (gain < gainctrl.maximum) + autogain_adjust(&gainctrl, &gain, steps, + gainctrl.maximum, 1); + else if (exposure < expoctrl.maximum) + autogain_adjust(&expoctrl, &exposure, steps, + expoctrl.maximum, 1); + else + steps = 0; + } + + if (steps) { + data->last_gain_correction = steps; + /* We are still settling down, force the next update sooner. Note we + skip the next frame as that is still captured with the old settings, + and another one just to be sure (because if we re-adjust based + on the old settings we might overshoot). */ + data->lookup_table_update_counter = V4L2PROCESSING_UPDATE_RATE - 2; + } + + if (gain != orig_gain) { + ctrl.id = V4L2_CID_GAIN; + ctrl.value = gain; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); + } + if (exposure != orig_exposure) { + ctrl.id = V4L2_CID_EXPOSURE; + ctrl.value = exposure; + SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); + } + + return 0; +} + +struct v4lprocessing_filter autogain_filter = { + autogain_active, autogain_calculate_lookup_tables +}; diff --git a/libv4lconvert/processing/gamma.c b/libv4lconvert/processing/gamma.c new file mode 100644 index 0000000..86e4038 --- /dev/null +++ b/libv4lconvert/processing/gamma.c @@ -0,0 +1,61 @@ +/* +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" + +#define CLIP(color) (unsigned char)(((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color))) + +static int gamma_active(struct v4lprocessing_data *data) +{ + int gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA); + + return gamma && gamma != 1000; +} + +static int gamma_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int i, x, gamma; + + gamma = v4lcontrol_get_ctrl(data->control, V4LCONTROL_GAMMA); + + if (gamma == 0) + return 0; + + if (gamma != data->last_gamma) { + for (i = 0; i < 256; i++) { + x = powf(i / 255.0, 1000.0 / gamma) * 255; + data->gamma_table[i] = CLIP(x); + } + data->last_gamma = gamma; + } + + for (i = 0; i < 256; i++) { + data->comp1[i] = data->gamma_table[data->comp1[i]]; + data->green[i] = data->gamma_table[data->green[i]]; + data->comp2[i] = data->gamma_table[data->comp2[i]]; + } + + return 1; +} + +struct v4lprocessing_filter gamma_filter = { + gamma_active, gamma_calculate_lookup_tables +}; diff --git a/libv4lconvert/processing/libv4lprocessing-priv.h b/libv4lconvert/processing/libv4lprocessing-priv.h new file mode 100644 index 0000000..f3fcc84 --- /dev/null +++ b/libv4lconvert/processing/libv4lprocessing-priv.h @@ -0,0 +1,67 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#ifndef __LIBV4LPROCESSING_PRIV_H +#define __LIBV4LPROCESSING_PRIV_H + +#include "../control/libv4lcontrol.h" +#include "../libv4lsyscall-priv.h" + +#define V4L2PROCESSING_UPDATE_RATE 10 + +struct v4lprocessing_data { + struct v4lcontrol_data *control; + int fd; + int do_process; + int controls_changed; + /* True if any of the lookup tables does not contain + linear 0-255 */ + int lookup_table_active; + /* Counts the number of processed frames until a + V4L2PROCESSING_UPDATE_RATE overflow happens */ + int lookup_table_update_counter; + /* RGB/BGR lookup tables */ + unsigned char comp1[256]; + unsigned char green[256]; + unsigned char comp2[256]; + /* Filter private data for filters which need it */ + /* whitebalance.c data */ + int green_avg; + int comp1_avg; + int comp2_avg; + /* gamma.c data */ + int last_gamma; + unsigned char gamma_table[256]; + /* autogain.c data */ + int last_gain_correction; +}; + +struct v4lprocessing_filter { + /* Returns 1 if the filter is active */ + int (*active)(struct v4lprocessing_data *data); + /* Returns 1 if any of the lookup tables was changed */ + int (*calculate_lookup_tables)(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); +}; + +extern struct v4lprocessing_filter whitebalance_filter; +extern struct v4lprocessing_filter autogain_filter; +extern struct v4lprocessing_filter gamma_filter; + +#endif diff --git a/libv4lconvert/processing/libv4lprocessing.c b/libv4lconvert/processing/libv4lprocessing.c new file mode 100644 index 0000000..c6d738d --- /dev/null +++ b/libv4lconvert/processing/libv4lprocessing.c @@ -0,0 +1,187 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +static struct v4lprocessing_filter *filters[] = { + &whitebalance_filter, + &autogain_filter, + &gamma_filter, +}; + +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data *control) +{ + struct v4lprocessing_data *data = + calloc(1, sizeof(struct v4lprocessing_data)); + + if (!data) { + fprintf(stderr, "libv4lprocessing: error: out of memory!\n"); + return NULL; + } + + data->fd = fd; + data->control = control; + + return data; +} + +void v4lprocessing_destroy(struct v4lprocessing_data *data) +{ + free(data); +} + +int v4lprocessing_pre_processing(struct v4lprocessing_data *data) +{ + int i; + + data->do_process = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) + data->do_process = 1; + } + + data->controls_changed |= v4lcontrol_controls_changed(data->control); + + return data->do_process; +} + +static void v4lprocessing_update_lookup_tables(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int i; + + for (i = 0; i < 256; i++) { + data->comp1[i] = i; + data->green[i] = i; + data->comp2[i] = i; + } + + data->lookup_table_active = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) { + if (filters[i]->calculate_lookup_tables(data, buf, fmt)) + data->lookup_table_active = 1; + } + } +} + +static void v4lprocessing_do_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + for (y = 0; (unsigned)y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp1[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; (unsigned)x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp2[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + break; + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + for (y = 0; (unsigned)y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; (unsigned)x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + break; + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + for (y = 0; (unsigned)y < fmt->fmt.pix.height; y++) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - 3 * fmt->fmt.pix.width; + } + break; + } +} + +void v4lprocessing_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + if (!data->do_process) + return; + + /* Do we support the current pixformat? */ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + break; + default: + return; /* Non supported pix format */ + } + + if (data->controls_changed || + data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) { + data->controls_changed = 0; + data->lookup_table_update_counter = 0; + /* Do this after resetting lookup_table_update_counter so that filters can + force the next update to be sooner when they changed camera settings */ + v4lprocessing_update_lookup_tables(data, buf, fmt); + } else + data->lookup_table_update_counter++; + + if (data->lookup_table_active) + v4lprocessing_do_processing(data, buf, fmt); + + data->do_process = 0; +} diff --git a/libv4lconvert/processing/libv4lprocessing.h b/libv4lconvert/processing/libv4lprocessing.h new file mode 100644 index 0000000..c702f2e --- /dev/null +++ b/libv4lconvert/processing/libv4lprocessing.h @@ -0,0 +1,42 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA +*/ + +#ifndef __LIBV4LPROCESSING_H +#define __LIBV4LPROCESSING_H + +#include "../libv4lsyscall-priv.h" +#include + +struct v4lprocessing_data; +struct v4lcontrol_data; + +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data *data); +void v4lprocessing_destroy(struct v4lprocessing_data *data); + +/* Prepare to process 1 frame, returns 1 if processing is necesary, + return 0 if no processing will be done */ +int v4lprocessing_pre_processing(struct v4lprocessing_data *data); + +/* Do the actual processing, this is a nop if v4lprocessing_pre_processing() + returned 0, or if called more then 1 time after a single + v4lprocessing_pre_processing() call. */ +void v4lprocessing_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); + +#endif diff --git a/libv4lconvert/processing/whitebalance.c b/libv4lconvert/processing/whitebalance.c new file mode 100644 index 0000000..5e8174e --- /dev/null +++ b/libv4lconvert/processing/whitebalance.c @@ -0,0 +1,209 @@ +/* +# (C) 2008-2009 Elmar Kleijn +# (C) 2008-2009 Sjoerd Piepenbrink +# (C) 2008-2009 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +#define CLIP256(color) (((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color))) +#define CLIP(color, min, max) (((color) > (max)) ? (max) : (((color) < (min)) ? (min) : (color))) + +static int whitebalance_active(struct v4lprocessing_data *data) +{ + int wb; + + wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); + if (!wb) { + /* Reset cached color averages */ + data->green_avg = 0; + } + + return wb; +} + +static int whitebalance_calculate_lookup_tables_generic( + struct v4lprocessing_data *data, int green_avg, int comp1_avg, int comp2_avg) +{ + int i, avg_avg; + const int threshold = 64; + const int max_step = 128; + + /* Clip averages (restricts maximum white balance correction) */ + green_avg = CLIP(green_avg, 512, 3072); + comp1_avg = CLIP(comp1_avg, 512, 3072); + comp2_avg = CLIP(comp2_avg, 512, 3072); + + /* First frame ? */ + if (data->green_avg == 0) { + data->green_avg = green_avg; + data->comp1_avg = comp1_avg; + data->comp2_avg = comp2_avg; + } else { + /* Slowly adjust the averages used for the correction, so that + we do not get a sudden change in colors */ + int throttling = 0; + + if (abs(data->green_avg - green_avg) > max_step) { + if (data->green_avg < green_avg) + data->green_avg += max_step; + else + data->green_avg -= max_step; + throttling = 1; + } else + data->green_avg = green_avg; + + if (abs(data->comp1_avg - comp1_avg) > max_step) { + if (data->comp1_avg < comp1_avg) + data->comp1_avg += max_step; + else + data->comp1_avg -= max_step; + throttling = 1; + } else + data->comp1_avg = comp1_avg; + + if (abs(data->comp2_avg - comp2_avg) > max_step) { + if (data->comp2_avg < comp2_avg) + data->comp2_avg += max_step; + else + data->comp2_avg -= max_step; + throttling = 1; + } else + data->comp2_avg = comp2_avg; + + /* + * If we are still converging to a stable update situation, + * re-calc the lookup tables next frame, but only if no + * other processing plugin has already asked for a shorter + * update cycle, as asking for an update each frame while + * some other pluging is trying to adjust hw settings is bad. + */ + if (throttling && data->lookup_table_update_counter == 0) + data->lookup_table_update_counter = + V4L2PROCESSING_UPDATE_RATE; + } + + if (abs(data->green_avg - data->comp1_avg) < threshold && + abs(data->green_avg - data->comp2_avg) < threshold && + abs(data->comp1_avg - data->comp2_avg) < threshold) + return 0; + + avg_avg = (data->green_avg + data->comp1_avg + data->comp2_avg) / 3; + + for (i = 0; i < 256; i++) { + data->comp1[i] = CLIP256(data->comp1[i] * avg_avg / data->comp1_avg); + data->green[i] = CLIP256(data->green[i] * avg_avg / data->green_avg); + data->comp2[i] = CLIP256(data->comp2[i] * avg_avg / data->comp2_avg); + } + + return 1; +} + +static int whitebalance_calculate_lookup_tables_bayer( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt, int starts_with_green) +{ + int x, y, a1 = 0, a2 = 0, b1 = 0, b2 = 0; + int green_avg, comp1_avg, comp2_avg; + + for (y = 0; (unsigned)y < fmt->fmt.pix.height; y += 2) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width; x += 2) { + a1 += *buf++; + a2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; (unsigned)x < fmt->fmt.pix.width; x += 2) { + b1 += *buf++; + b2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + + if (starts_with_green) { + green_avg = a1 / 2 + b2 / 2; + comp1_avg = a2; + comp2_avg = b1; + } else { + green_avg = a2 / 2 + b1 / 2; + comp1_avg = a1; + comp2_avg = b2; + } + + /* Norm avg to ~ 0 - 4095 */ + green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + + return whitebalance_calculate_lookup_tables_generic(data, green_avg, + comp1_avg, comp2_avg); +} + +static int whitebalance_calculate_lookup_tables_rgb( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt) +{ + int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0; + + for (y = 0; (unsigned)y < fmt->fmt.pix.height; y++) { + for (x = 0; (unsigned)x < fmt->fmt.pix.width; x++) { + comp1_avg += *buf++; + green_avg += *buf++; + comp2_avg += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3; + } + + /* Norm avg to ~ 0 - 4095 */ + green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16; + + return whitebalance_calculate_lookup_tables_generic(data, green_avg, + comp1_avg, comp2_avg); +} + + +static int whitebalance_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 1); + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 0); + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return whitebalance_calculate_lookup_tables_rgb(data, buf, fmt); + } + + return 0; /* Should never happen */ +} + +struct v4lprocessing_filter whitebalance_filter = { + whitebalance_active, whitebalance_calculate_lookup_tables +}; diff --git a/libv4lconvert/rgbyuv.c b/libv4lconvert/rgbyuv.c new file mode 100644 index 0000000..f2b2ecf --- /dev/null +++ b/libv4lconvert/rgbyuv.c @@ -0,0 +1,756 @@ +/* + +# RGB <-> YUV conversion routines +# (C) 2008 Hans de Goede + +# RGB565 conversion routines +# (C) 2009 Mauro Carvalho Chehab + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + + */ + +#include +#include "libv4lconvert-priv.h" + +#define RGB2Y(r, g, b, y) \ + (y) = ((8453 * (r) + 16594 * (g) + 3223 * (b) + 524288) >> 15) + +#define RGB2UV(r, g, b, u, v) \ + do { \ + (u) = ((-4878 * (r) - 9578 * (g) + 14456 * (b) + 4210688) >> 15); \ + (v) = ((14456 * (r) - 12105 * (g) - 2351 * (b) + 4210688) >> 15); \ + } while (0) + +void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int bgr, int yvu, int bpp) +{ + unsigned int x, y; + unsigned char *udest, *vdest; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + for (x = 0; x < src_fmt->fmt.pix.width; x++) { + if (bgr) + RGB2Y(src[2], src[1], src[0], *dest++); + else + RGB2Y(src[0], src[1], src[2], *dest++); + src += bpp; + } + + src += src_fmt->fmt.pix.bytesperline - bpp * src_fmt->fmt.pix.width; + } + src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline; + + /* U + V */ + if (yvu) { + vdest = dest; + udest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } else { + udest = dest; + vdest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < src_fmt->fmt.pix.width / 2; x++) { + int avg_src[3]; + + avg_src[0] = (src[0] + src[bpp] + src[src_fmt->fmt.pix.bytesperline] + + src[src_fmt->fmt.pix.bytesperline + bpp]) / 4; + avg_src[1] = (src[1] + src[bpp + 1] + src[src_fmt->fmt.pix.bytesperline + 1] + + src[src_fmt->fmt.pix.bytesperline + bpp + 1]) / 4; + avg_src[2] = (src[2] + src[bpp + 2] + src[src_fmt->fmt.pix.bytesperline + 2] + + src[src_fmt->fmt.pix.bytesperline + bpp + 2]) / 4; + if (bgr) + RGB2UV(avg_src[2], avg_src[1], avg_src[0], *udest++, *vdest++); + else + RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++); + src += 2 * bpp; + } + src += 2 * src_fmt->fmt.pix.bytesperline - bpp * src_fmt->fmt.pix.width; + } +} + +#define YUV2R(y, u, v) ({ \ + int r = (y) + ((((v) - 128) * 1436) >> 10); r > 255 ? 255 : r < 0 ? 0 : r; }) +#define YUV2G(y, u, v) ({ \ + int g = (y) - ((((u) - 128) * 352 + ((v) - 128) * 731) >> 10); g > 255 ? 255 : g < 0 ? 0 : g; }) +#define YUV2B(y, u, v) ({ \ + int b = (y) + ((((u) - 128) * 1814) >> 10); b > 255 ? 255 : b < 0 ? 0 : b; }) + +#define CLIP(color) (unsigned char)(((color) > 0xFF) ? 0xff : (((color) < 0) ? 0 : (color))) + +void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height, int yvu) +{ + int i, j; + + const unsigned char *ysrc = src; + const unsigned char *usrc, *vsrc; + + if (yvu) { + vsrc = src + width * height; + usrc = vsrc + (width * height) / 4; + } else { + usrc = src + width * height; + vsrc = usrc + (width * height) / 4; + } + + for (i = 0; i < height; i++) { + for (j = 0; j < width; j += 2) { +#if 1 /* fast slightly less accurate multiplication free code */ + int u1 = (((*usrc - 128) << 7) + (*usrc - 128)) >> 6; + int rg = (((*usrc - 128) << 1) + (*usrc - 128) + + ((*vsrc - 128) << 2) + ((*vsrc - 128) << 1)) >> 3; + int v1 = (((*vsrc - 128) << 1) + (*vsrc - 128)) >> 1; + + *dest++ = CLIP(*ysrc + u1); + *dest++ = CLIP(*ysrc - rg); + *dest++ = CLIP(*ysrc + v1); + ysrc++; + + *dest++ = CLIP(*ysrc + u1); + *dest++ = CLIP(*ysrc - rg); + *dest++ = CLIP(*ysrc + v1); +#else + *dest++ = YUV2B(*ysrc, *usrc, *vsrc); + *dest++ = YUV2G(*ysrc, *usrc, *vsrc); + *dest++ = YUV2R(*ysrc, *usrc, *vsrc); + ysrc++; + + *dest++ = YUV2B(*ysrc, *usrc, *vsrc); + *dest++ = YUV2G(*ysrc, *usrc, *vsrc); + *dest++ = YUV2R(*ysrc, *usrc, *vsrc); +#endif + ysrc++; + usrc++; + vsrc++; + } + /* Rewind u and v for next line */ + if (!(i & 1)) { + usrc -= width / 2; + vsrc -= width / 2; + } + } +} + +void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int yvu) +{ + int i, j; + + const unsigned char *ysrc = src; + const unsigned char *usrc, *vsrc; + + if (yvu) { + vsrc = src + width * height; + usrc = vsrc + (width * height) / 4; + } else { + usrc = src + width * height; + vsrc = usrc + (width * height) / 4; + } + + for (i = 0; i < height; i++) { + for (j = 0; j < width; j += 2) { +#if 1 /* fast slightly less accurate multiplication free code */ + int u1 = (((*usrc - 128) << 7) + (*usrc - 128)) >> 6; + int rg = (((*usrc - 128) << 1) + (*usrc - 128) + + ((*vsrc - 128) << 2) + ((*vsrc - 128) << 1)) >> 3; + int v1 = (((*vsrc - 128) << 1) + (*vsrc - 128)) >> 1; + + *dest++ = CLIP(*ysrc + v1); + *dest++ = CLIP(*ysrc - rg); + *dest++ = CLIP(*ysrc + u1); + ysrc++; + + *dest++ = CLIP(*ysrc + v1); + *dest++ = CLIP(*ysrc - rg); + *dest++ = CLIP(*ysrc + u1); +#else + *dest++ = YUV2R(*ysrc, *usrc, *vsrc); + *dest++ = YUV2G(*ysrc, *usrc, *vsrc); + *dest++ = YUV2B(*ysrc, *usrc, *vsrc); + ysrc++; + + *dest++ = YUV2R(*ysrc, *usrc, *vsrc); + *dest++ = YUV2G(*ysrc, *usrc, *vsrc); + *dest++ = YUV2B(*ysrc, *usrc, *vsrc); +#endif + ysrc++; + usrc++; + vsrc++; + } + /* Rewind u and v for next line */ + if (!(i&1)) { + usrc -= width / 2; + vsrc -= width / 2; + } + } +} + +void v4lconvert_yuyv_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height, int stride) +{ + int j; + + while (--height >= 0) { + for (j = 0; j + 1 < width; j += 2) { + int u = src[1]; + int v = src[3]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[0] + u1); + *dest++ = CLIP(src[0] - rg); + *dest++ = CLIP(src[0] + v1); + + *dest++ = CLIP(src[2] + u1); + *dest++ = CLIP(src[2] - rg); + *dest++ = CLIP(src[2] + v1); + src += 4; + } + src += stride - width * 2; + } +} + +void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int stride) +{ + int j; + + while (--height >= 0) { + for (j = 0; j + 1 < width; j += 2) { + int u = src[1]; + int v = src[3]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[0] + v1); + *dest++ = CLIP(src[0] - rg); + *dest++ = CLIP(src[0] + u1); + + *dest++ = CLIP(src[2] + v1); + *dest++ = CLIP(src[2] - rg); + *dest++ = CLIP(src[2] + u1); + src += 4; + } + src += stride - (width * 2); + } +} + +void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest, + int width, int height, int stride, int yvu) +{ + int i, j; + const unsigned char *src1; + unsigned char *udest, *vdest; + + /* copy the Y values */ + src1 = src; + for (i = 0; i < height; i++) { + for (j = 0; j + 1 < width; j += 2) { + *dest++ = src1[0]; + *dest++ = src1[2]; + src1 += 4; + } + src1 += stride - width * 2; + } + + /* copy the U and V values */ + src++; /* point to V */ + src1 = src + stride; /* next line */ + if (yvu) { + vdest = dest; + udest = dest + width * height / 4; + } else { + udest = dest; + vdest = dest + width * height / 4; + } + for (i = 0; i < height; i += 2) { + for (j = 0; j + 1 < width; j += 2) { + *udest++ = ((int) src[0] + src1[0]) / 2; /* U */ + *vdest++ = ((int) src[2] + src1[2]) / 2; /* V */ + src += 4; + src1 += 4; + } + src1 += stride - width * 2; + src = src1; + src1 += stride; + } +} + +void v4lconvert_yvyu_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height, int stride) +{ + int j; + + while (--height >= 0) { + for (j = 0; j + 1 < width; j += 2) { + int u = src[3]; + int v = src[1]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[0] + u1); + *dest++ = CLIP(src[0] - rg); + *dest++ = CLIP(src[0] + v1); + + *dest++ = CLIP(src[2] + u1); + *dest++ = CLIP(src[2] - rg); + *dest++ = CLIP(src[2] + v1); + src += 4; + } + src += stride - (width * 2); + } +} + +void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int stride) +{ + int j; + + while (--height >= 0) { + for (j = 0; j + 1 < width; j += 2) { + int u = src[3]; + int v = src[1]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[0] + v1); + *dest++ = CLIP(src[0] - rg); + *dest++ = CLIP(src[0] + u1); + + *dest++ = CLIP(src[2] + v1); + *dest++ = CLIP(src[2] - rg); + *dest++ = CLIP(src[2] + u1); + src += 4; + } + src += stride - (width * 2); + } +} + +void v4lconvert_uyvy_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height, int stride) +{ + int j; + + while (--height >= 0) { + for (j = 0; j + 1 < width; j += 2) { + int u = src[0]; + int v = src[2]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[1] + u1); + *dest++ = CLIP(src[1] - rg); + *dest++ = CLIP(src[1] + v1); + + *dest++ = CLIP(src[3] + u1); + *dest++ = CLIP(src[3] - rg); + *dest++ = CLIP(src[3] + v1); + src += 4; + } + src += stride - width * 2; + } +} + +void v4lconvert_uyvy_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int stride) +{ + int j; + + while (--height >= 0) { + for (j = 0; j + 1 < width; j += 2) { + int u = src[0]; + int v = src[2]; + int u1 = (((u - 128) << 7) + (u - 128)) >> 6; + int rg = (((u - 128) << 1) + (u - 128) + + ((v - 128) << 2) + ((v - 128) << 1)) >> 3; + int v1 = (((v - 128) << 1) + (v - 128)) >> 1; + + *dest++ = CLIP(src[1] + v1); + *dest++ = CLIP(src[1] - rg); + *dest++ = CLIP(src[1] + u1); + + *dest++ = CLIP(src[3] + v1); + *dest++ = CLIP(src[3] - rg); + *dest++ = CLIP(src[3] + u1); + src += 4; + } + src += stride - width * 2; + } +} + +void v4lconvert_uyvy_to_yuv420(const unsigned char *src, unsigned char *dest, + int width, int height, int stride, int yvu) +{ + int i, j; + const unsigned char *src1; + unsigned char *udest, *vdest; + + /* copy the Y values */ + src1 = src; + for (i = 0; i < height; i++) { + for (j = 0; j + 1 < width; j += 2) { + *dest++ = src1[1]; + *dest++ = src1[3]; + src1 += 4; + } + src1 += stride - width * 2; + } + + /* copy the U and V values */ + src1 = src + stride; /* next line */ + if (yvu) { + vdest = dest; + udest = dest + width * height / 4; + } else { + udest = dest; + vdest = dest + width * height / 4; + } + for (i = 0; i < height; i += 2) { + for (j = 0; j + 1 < width; j += 2) { + *udest++ = ((int) src[0] + src1[0]) / 2; /* U */ + *vdest++ = ((int) src[2] + src1[2]) / 2; /* V */ + src += 4; + src1 += 4; + } + src1 += stride - width * 2; + src = src1; + src1 += stride; + } +} + +void v4lconvert_swap_rgb(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int i; + + for (i = 0; i < (width * height); i++) { + unsigned char tmp0, tmp1; + tmp0 = *src++; + tmp1 = *src++; + *dst++ = *src++; + *dst++ = tmp1; + *dst++ = tmp0; + } +} + +void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt) +{ + unsigned int y; + + /* Copy Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + memcpy(dest, src, src_fmt->fmt.pix.width); + dest += src_fmt->fmt.pix.width; + src += src_fmt->fmt.pix.bytesperline; + } + + /* Copy component 2 */ + src += src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 4; + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + dest += src_fmt->fmt.pix.width / 2; + src += src_fmt->fmt.pix.bytesperline / 2; + } + + /* Copy component 1 */ + src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline / 2; + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + memcpy(dest, src, src_fmt->fmt.pix.width / 2); + dest += src_fmt->fmt.pix.width / 2; + src += src_fmt->fmt.pix.bytesperline / 2; + } +} + +void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + unsigned short tmp = *(unsigned short *)src; + + /* Original format: rrrrrggg gggbbbbb */ + *dest++ = 0xf8 & (tmp >> 8); + *dest++ = 0xfc & (tmp >> 3); + *dest++ = 0xf8 & (tmp << 3); + + src += 2; + } + } +} + +void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + unsigned short tmp = *(unsigned short *)src; + + /* Original format: rrrrrggg gggbbbbb */ + *dest++ = 0xf8 & (tmp << 3); + *dest++ = 0xfc & (tmp >> 3); + *dest++ = 0xf8 & (tmp >> 8); + + src += 2; + } + } +} + +void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int yvu) +{ + unsigned int x, y; + unsigned short tmp; + unsigned char *udest, *vdest; + unsigned r[4], g[4], b[4]; + int avg_src[3]; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) { + for (x = 0; x < src_fmt->fmt.pix.width; x++) { + tmp = *(unsigned short *)src; + r[0] = 0xf8 & (tmp << 3); + g[0] = 0xfc & (tmp >> 3); + b[0] = 0xf8 & (tmp >> 8); + RGB2Y(r[0], g[0], b[0], *dest++); + src += 2; + } + src += src_fmt->fmt.pix.bytesperline - 2 * src_fmt->fmt.pix.width; + } + src -= src_fmt->fmt.pix.height * src_fmt->fmt.pix.bytesperline; + + /* U + V */ + if (yvu) { + vdest = dest; + udest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } else { + udest = dest; + vdest = dest + src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 4; + } + + for (y = 0; y < src_fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < src_fmt->fmt.pix.width / 2; x++) { + tmp = *(unsigned short *)src; + r[0] = 0xf8 & (tmp << 3); + g[0] = 0xfc & (tmp >> 3); + b[0] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + 1); + r[1] = 0xf8 & (tmp << 3); + g[1] = 0xfc & (tmp >> 3); + b[1] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline); + r[2] = 0xf8 & (tmp << 3); + g[2] = 0xfc & (tmp >> 3); + b[2] = 0xf8 & (tmp >> 8); + + tmp = *(((unsigned short *)src) + src_fmt->fmt.pix.bytesperline + 1); + r[3] = 0xf8 & (tmp << 3); + g[3] = 0xfc & (tmp >> 3); + b[3] = 0xf8 & (tmp >> 8); + + avg_src[0] = (r[0] + r[1] + r[2] + r[3]) / 4; + avg_src[1] = (g[0] + g[1] + g[2] + g[3]) / 4; + avg_src[2] = (b[0] + b[1] + b[2] + b[3]) / 4; + RGB2UV(avg_src[0], avg_src[1], avg_src[2], *udest++, *vdest++); + src += 4; + } + src += 2 * src_fmt->fmt.pix.bytesperline - 2 * src_fmt->fmt.pix.width; + } +} + +void v4lconvert_y16_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height, int little_endian) +{ + int j; + + if (little_endian) + src++; + + while (--height >= 0) { + for (j = 0; j < width; j++) { + *dest++ = *src; + *dest++ = *src; + *dest++ = *src; + src+=2; + } + } +} + +void v4lconvert_y16_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt, int little_endian) +{ + unsigned int x, y; + + if (little_endian) + src++; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) + for (x = 0; x < src_fmt->fmt.pix.width; x++){ + *dest++ = *src; + src+=2; + } + + /* Clear U/V */ + memset(dest, 0x80, src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 2); +} + +void v4lconvert_grey_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + *dest++ = *src; + *dest++ = *src; + *dest++ = *src; + src++; + } + } +} + +void v4lconvert_grey_to_yuv420(const unsigned char *src, unsigned char *dest, + const struct v4l2_format *src_fmt) +{ + unsigned int x, y; + + /* Y */ + for (y = 0; y < src_fmt->fmt.pix.height; y++) + for (x = 0; x < src_fmt->fmt.pix.width; x++) + *dest++ = *src++; + + /* Clear U/V */ + memset(dest, 0x80, src_fmt->fmt.pix.width * src_fmt->fmt.pix.height / 2); +} + +/* Unpack buffer of (vw bit) data into padded 16bit buffer. */ +static inline void convert_packed_to_16bit(const uint8_t *raw, uint16_t *unpacked, + int vw, int unpacked_len) +{ + int mask = (1 << vw) - 1; + uint32_t buffer = 0; + int bitsIn = 0; + while (unpacked_len--) { + while (bitsIn < vw) { + buffer = (buffer << 8) | *(raw++); + bitsIn += 8; + } + bitsIn -= vw; + *(unpacked++) = (buffer >> bitsIn) & mask; + } +} + +int v4lconvert_y10b_to_rgb24(struct v4lconvert_data *data, + const unsigned char *src, unsigned char *dest, int width, int height) +{ + unsigned char *unpacked_buffer; + + unpacked_buffer = v4lconvert_alloc_buffer(width * height * 2, + &data->convert_pixfmt_buf, + &data->convert_pixfmt_buf_size); + if (!unpacked_buffer) + return v4lconvert_oom_error(data); + + convert_packed_to_16bit((uint8_t *)src, (uint16_t *)unpacked_buffer, + 10, width * height); + + int j; + unsigned short *tmp = (unsigned short *)unpacked_buffer; + while (--height >= 0) { + for (j = 0; j < width; j++) { + + /* Only 10 useful bits, so we discard the LSBs */ + *dest++ = (*tmp & 0x3ff) >> 2; + *dest++ = (*tmp & 0x3ff) >> 2; + *dest++ = (*tmp & 0x3ff) >> 2; + + /* +1 means two bytes as we are dealing with (unsigned short) */ + tmp += 1; + } + } + return 0; +} + +int v4lconvert_y10b_to_yuv420(struct v4lconvert_data *data, + const unsigned char *src, unsigned char *dest, int width, int height) +{ + unsigned char *unpacked_buffer; + + unpacked_buffer = v4lconvert_alloc_buffer(width * height * 2, + &data->convert_pixfmt_buf, + &data->convert_pixfmt_buf_size); + if (!unpacked_buffer) + return v4lconvert_oom_error(data); + + convert_packed_to_16bit((uint8_t *)src, (uint16_t *)unpacked_buffer, + 10, width * height); + + int x, y; + unsigned short *tmp = (unsigned short *)unpacked_buffer; + + /* Y */ + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) { + + /* Only 10 useful bits, so we discard the LSBs */ + *dest++ = (*tmp & 0x3ff) >> 2; + + /* +1 means two bytes as we are dealing with (unsigned short) */ + tmp += 1; + } + + /* Clear U/V */ + memset(dest, 0x80, width * height / 2); + + return 0; +} + +void v4lconvert_rgb32_to_rgb24(const unsigned char *src, unsigned char *dest, + int width, int height,int bgr) +{ + int j; + while (--height >= 0) { + for (j = 0; j < width; j++) { + if (bgr){ + *dest++ = src[2]; + *dest++ = src[1]; + *dest++ = src[0]; + src+=4; + } + else{ + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + src+=1; + } + } + } +} diff --git a/libv4lconvert/se401.c b/libv4lconvert/se401.c new file mode 100644 index 0000000..8893145 --- /dev/null +++ b/libv4lconvert/se401.c @@ -0,0 +1,164 @@ +/* +# (C) 2011 Hans de Goede + +# The compression algorithm has been taken from the v4l1 se401 linux kernel +# driver by Jeroen B. Vreeken (pe1rxq@amsat.org) + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include "libv4lconvert-priv.h" +#include + +/* The se401 compression algorithm uses a fixed quant factor, which + can be configured by setting the high nibble of the SE401_OPERATINGMODE + feature. This needs to exactly match what is in the SE401 driver! */ +#define SE401_QUANT_FACT 8 + +static void wr_pixel(int p, uint8_t **dest, int pitch, int *x) +{ + int i = *x; + + /* First 3 pixels of each line are absolute */ + if (i < 3) { + (*dest)[i] = p * SE401_QUANT_FACT; + } else { + (*dest)[i] = (*dest)[i - 3] + p * SE401_QUANT_FACT; + } + + *x += 1; + if (*x == pitch) { + *x = 0; + *dest += pitch; + } +} + +enum decode_state { + get_len, + sign_bit, + other_bits, +}; + +static int decode_JangGu(const uint8_t *data, int bits, int plen, int pixels, + uint8_t **dest, int pitch, int *x) +{ + enum decode_state state = get_len; + int len = 0; + int value = 0; + int bitnr; + int bit; + + while (plen) { + bitnr = 8; + while (bitnr && bits) { + bit = ((*data) >> (bitnr-1))&1; + switch (state) { + case get_len: + if (bit) { + len++; + } else { + if (!len) { + wr_pixel(0, dest, pitch, x); + if (!--pixels) + return 0; + } else { + state = sign_bit; + value = 0; + } + } + break; + case sign_bit: + if (bit) + value = 0; + else + value = -(1 << len) + 1; + state = other_bits; + /* fall through for positive number and + len == 1 handling */ + case other_bits: + len--; + value += bit << len; + if (len == 0) { + /* Done write pixel and get bit len of + the next one */ + state = get_len; + wr_pixel(value, dest, pitch, x); + if (!--pixels) + return 0; + } + break; + } + bitnr--; + bits--; + } + data++; + plen--; + } + return -1; +} + +int v4lconvert_se401_to_rgb24(struct v4lconvert_data *data, + const unsigned char *src, int src_size, + unsigned char *dest, int width, int height) +{ + int in, plen, bits, pixels, info; + int x = 0, total_pixels = 0; + + if (!src || !dest) + goto error; + + for (in = 0; in + 4 < src_size; in += plen) { + bits = src[in + 3] + (src[in + 2] << 8); + pixels = src[in + 1] + ((src[in + 0] & 0x3f) << 8); + info = (src[in + 0] & 0xc0) >> 6; + plen = ((bits + 47) >> 4) << 1; + /* Sanity checks */ + if (plen > 1024) { + V4LCONVERT_ERR("invalid se401 packet len %d", plen); + goto error; + } + if (in + plen > src_size) { + V4LCONVERT_ERR("incomplete se401 packet"); + goto error; + } + if (total_pixels + pixels > width * height) { + V4LCONVERT_ERR("se401 frame overflow"); + goto error; + } + /* info: 0 inter packet, 1 eof, 2 sof, 3 not used */ + if ((in == 0 && info != 2) || + (in > 0 && in + plen < src_size && info != 0) || + (in + plen == src_size && info != 1)) { + V4LCONVERT_ERR("invalid se401 frame info value"); + goto error; + } + if (decode_JangGu(&src[in + 4], bits, plen, pixels * 3, + &dest, width * 3, &x)) { + V4LCONVERT_ERR("short se401 packet"); + goto error; + } + total_pixels += pixels; + } + + if (in != src_size || total_pixels != width * height) { + V4LCONVERT_ERR("se401 frame size mismatch"); + goto error; + } + + return 0; + +error: + errno = EIO; + return -1; +} diff --git a/libv4lconvert/sn9c10x.c b/libv4lconvert/sn9c10x.c new file mode 100644 index 0000000..a835bf6 --- /dev/null +++ b/libv4lconvert/sn9c10x.c @@ -0,0 +1,273 @@ +/* +# sonix decoder +# Bertrik.Sikken. (C) 2005 + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + +# Note this code was originally licensed under the GNU GPL instead of the +# GNU LGPL, its license has been changed with permission, see the permission +# mail at the end of this file. + */ + +#include "libv4lconvert-priv.h" + +#define CLAMP(x) ((x) < 0 ? 0 : ((x) > 255) ? 255 : (x)) + +struct code_table { + int is_abs; + int len; + int val; + int unk; +}; + + +/* local storage */ +/* FIXME not thread safe !! */ +static struct code_table table[256]; +static int init_done; + +/* + sonix_decompress_init + ===================== + pre-calculates a locally stored table for efficient huffman-decoding. + + Each entry at index x in the table represents the codeword + present at the MSB of byte x. + + */ +static void sonix_decompress_init(void) +{ + int i; + int is_abs, val, len, unk; + + for (i = 0; i < 256; i++) { + is_abs = 0; + val = 0; + len = 0; + unk = 0; + if ((i & 0x80) == 0) { + /* code 0 */ + val = 0; + len = 1; + } else if ((i & 0xE0) == 0x80) { + /* code 100 */ + val = 4; + len = 3; + } else if ((i & 0xE0) == 0xA0) { + /* code 101 */ + val = -4; + len = 3; + } else if ((i & 0xF0) == 0xD0) { + /* code 1101 */ + val = 11; + len = 4; + } else if ((i & 0xF0) == 0xF0) { + /* code 1111 */ + val = -11; + len = 4; + } else if ((i & 0xF8) == 0xC8) { + /* code 11001 */ + val = 20; + len = 5; + } else if ((i & 0xFC) == 0xC0) { + /* code 110000 */ + val = -20; + len = 6; + } else if ((i & 0xFC) == 0xC4) { + /* code 110001xx: unknown */ + val = 0; + len = 8; + unk = 1; + } else if ((i & 0xF0) == 0xE0) { + /* code 1110xxxx */ + is_abs = 1; + val = (i & 0x0F) << 4; + len = 8; + } + table[i].is_abs = is_abs; + table[i].val = val; + table[i].len = len; + table[i].unk = unk; + } + + init_done = 1; +} + + +/* + sonix_decompress + ================ + decompresses an image encoded by a SN9C101 camera controller chip. + + IN width + height + inp pointer to compressed frame (with header already stripped) + OUT outp pointer to decompressed frame + + Returns 0 if the operation was successful. + Returns <0 if operation failed. + + */ +void v4lconvert_decode_sn9c10x(const unsigned char *inp, unsigned char *outp, + int width, int height) +{ + int row, col; + int val; + int bitpos; + unsigned char code; + const unsigned char *addr; + + if (!init_done) + sonix_decompress_init(); + + bitpos = 0; + for (row = 0; row < height; row++) { + col = 0; + + /* first two pixels in first two rows are stored as raw 8-bit */ + if (row < 2) { + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + bitpos += 8; + *outp++ = code; + + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + bitpos += 8; + *outp++ = code; + + col += 2; + } + + while (col < width) { + /* get bitcode from bitstream */ + addr = inp + (bitpos >> 3); + code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); + + /* update bit position */ + bitpos += table[code].len; + + /* Skip unknown codes (most likely they indicate + a change of the delta's the various codes encode) */ + if (table[code].unk) + continue; + + /* calculate pixel value */ + val = table[code].val; + if (!table[code].is_abs) { + /* value is relative to top and left pixel */ + if (col < 2) { + /* left column: relative to top pixel */ + val += outp[-2 * width]; + } else if (row < 2) { + /* top row: relative to left pixel */ + val += outp[-2]; + } else { + /* main area: average of left pixel and top pixel */ + val += (outp[-2] + outp[-2 * width]) / 2; + } + } + + /* store pixel */ + *outp++ = CLAMP(val); + col++; + } + } +} + +/* + Return-Path: +Received: from koko.hhs.nl ([145.52.2.16] verified) +by hhs.nl (CommuniGate Pro SMTP 4.3.6) +with ESMTP id 89132066 for j.w.r.degoede@hhs.nl; Thu, 03 Jul 2008 15:19:55 +0200 +Received: from exim (helo=koko) +by koko.hhs.nl with local-smtp (Exim 4.62) +(envelope-from ) +id 1KEOj5-0000nq-KR +for j.w.r.degoede@hhs.nl; Thu, 03 Jul 2008 15:19:55 +0200 +Received: from [192.87.102.69] (port=33783 helo=filter1-ams.mf.surf.net) +by koko.hhs.nl with esmtp (Exim 4.62) +(envelope-from ) +id 1KEOj5-0000nj-7r +for j.w.r.degoede@hhs.nl; Thu, 03 Jul 2008 15:19:55 +0200 +Received: from cardassian.kabelfoon.nl (cardassian3.kabelfoon.nl [62.45.45.105]) +by filter1-ams.mf.surf.net (8.13.8/8.13.8/Debian-3) with ESMTP id m63DJsKW032598 +for ; Thu, 3 Jul 2008 15:19:54 +0200 +Received: from [192.168.1.1] (044-013-045-062.dynamic.caiway.nl [62.45.13.44]) +by cardassian.kabelfoon.nl (Postfix) with ESMTP id 77761341D9A +for ; Thu, 3 Jul 2008 15:19:54 +0200 (CEST) +Message-ID: <486CD1F9.8000307@sikken.nl> +Date: Thu, 03 Jul 2008 15:19:53 +0200 +From: Bertrik Sikken +User-Agent: Thunderbird 2.0.0.14 (Windows/20080421) +MIME-Version: 1.0 +To: Hans de Goede +Subject: Re: pac207 bayer decompression algorithm license question +References: <48633F02.3040108@hhs.nl> <4863F611.80104@sikken.nl> <486CC6AF.7050509@hhs.nl> +In-Reply-To: <486CC6AF.7050509@hhs.nl> +X-Enigmail-Version: 0.95.6 +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 7bit +X-Canit-CHI2: 0.00 +X-Bayes-Prob: 0.0001 (Score 0, tokens from: @@RPTN) +X-Spam-Score: 0.00 () [Tag at 8.00] +X-CanItPRO-Stream: hhs:j.w.r.degoede@hhs.nl (inherits from hhs:default,base:default) +X-Canit-Stats-ID: 90943081 - 6a9ff19e8165 +X-Scanned-By: CanIt (www . roaringpenguin . com) on 192.87.102.69 +X-Anti-Virus: Kaspersky Anti-Virus for MailServers 5.5.2/RELEASE, bases: 03072008 #811719, status: clean + +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +Hans de Goede wrote: +| Bertrik Sikken wrote: +|> Hallo Hans, +|> +|> Hans de Goede wrote: +|>> I would like to also add support for decompressing the pac207's +|>> compressed +|>> bayer to this lib (and remove it from the kernel driver) and I've +|>> heard from Thomas Kaiser that you are a co-author of the +|>> decompression code. In order to add support for decompressing pac207 +|>> compressed bayer to libv4l I need +|>> permission to relicense the decompression code under the LGPL +|>> (version 2 or later). +|>> +|>> Can you give me permission for this? +|> +|> Ja, vind ik goed. +|> +| +| Thanks! +| +| I'm currently working on adding support for the sn9c10x bayer +| compression to libv4l too, and I noticed this was written by you too. +| +| May I have your permission to relicense the sn9c10x bayer decompression +| code under the LGPL (version 2 or later)? + +I hereby grant you permission to relicense the sn9c10x bayer +decompression code under the LGPL (version 2 or later). + +Kind regards, + Bertrik + -----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.7 (MingW32) + Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org + + iD8DBQFIbNH5ETD6mlrWxPURAipvAJ9sv1ZpHyb81NMFejr6x0wqHX3i7QCfRDoB + jZi2e5lUjEh5KvS0dqXbi9I= + =KQfR + -----END PGP SIGNATURE----- + */ diff --git a/libv4lconvert/sn9c2028-decomp.c b/libv4lconvert/sn9c2028-decomp.c new file mode 100644 index 0000000..e95d1ac --- /dev/null +++ b/libv4lconvert/sn9c2028-decomp.c @@ -0,0 +1,154 @@ +/* + * sn9c2028-decomp.c + * + * Decompression function for the Sonix SN9C2028 dual-mode cameras. + * + * Code adapted from libgphoto2/camlibs/sonix, original version of which was + * Copyright (c) 2005 Theodore Kilgore + * + * History: + * + * This decoding algorithm originates from the work of Bertrik Sikken for the + * SN9C102 cameras. This version is an adaptation of work done by Mattias + * Krauss for the webcam-osx (macam) project. There, it was further adapted + * for use with the Vivitar Vivicam 3350B (an SN9C2028 camera) by + * Harald Ruda . Harald brought to my attention the + * work done in the macam project and suggested that I use it. One improvement + * of my own was to notice that the even and odd columns of the image have been + * reversed by the decompression algorithm, and this needs to be corrected + * during the decompression. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, + * Boston, MA 02111-1307, USA. + */ + +#include "libv4lconvert-priv.h" + +/* Four defines for bitstream operations, used in the decode function */ + +#define PEEK_BITS(num, to) { \ + if (bitBufCount < num) { \ + do { \ + bitBuf = (bitBuf << 8) | (*(src++)); \ + bitBufCount += 8; \ + } while (bitBufCount < 24); \ + } \ + to = bitBuf >> (bitBufCount - num); \ +} + +/* + * PEEK_BITS puts the next bits into the low bits of . + * when the buffer is empty, it is completely refilled. + * This strategy tries to reduce memory access. Note that the high bits + * are NOT set to zero! + */ + +#define EAT_BITS(num) { bitBufCount -= num; bits_eaten += num; } + +/* + * EAT_BITS consumes bits (PEEK_BITS does not consume anything, + * it just peeks) + */ + +#define PARSE_PIXEL(val) {\ + PEEK_BITS(10, bits);\ + if ((bits & 0x200) == 0) {\ + EAT_BITS(1);\ + } \ + else if ((bits & 0x380) == 0x280) {\ + EAT_BITS(3);\ + val += 3;\ + if (val > 255)\ + val = 255;\ + } \ + else if ((bits & 0x380) == 0x300) {\ + EAT_BITS(3);\ + val -= 3;\ + if (val < 0)\ + val = 0;\ + } \ + else if ((bits & 0x3c0) == 0x200) {\ + EAT_BITS(4);\ + val += 8;\ + if (val > 255)\ + val = 255;\ + } \ + else if ((bits & 0x3c0) == 0x240) {\ + EAT_BITS(4);\ + val -= 8;\ + if (val < 0)\ + val = 0;\ + } \ + else if ((bits & 0x3c0) == 0x3c0) {\ + EAT_BITS(4);\ + val -= 20;\ + if (val < 0)\ + val = 0;\ + } \ + else if ((bits & 0x3e0) == 0x380) {\ + EAT_BITS(5);\ + val += 20;\ + if (val > 255)\ + val = 255;\ + } \ + else {\ + EAT_BITS(10);\ + val = 8 * (bits & 0x1f);\ + } \ +} + + +#define PUT_PIXEL_PAIR {\ + long pp;\ + pp = (c1val << 8) + c2val;\ + *((unsigned short *)(dst + dst_index)) = pp;\ + dst_index += 2;\ +} + +/* Now the decode function itself */ + +void v4lconvert_decode_sn9c2028(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + long dst_index = 0; + int starting_row = 0; + unsigned short bits; + short c1val, c2val; + int x, y; + unsigned long bitBuf = 0; + unsigned long bitBufCount = 0; + unsigned long bits_eaten = 0; + + src += 12; /* Remove the header */ + + for (y = starting_row; y < height; y++) { + PEEK_BITS(8, bits); + EAT_BITS(8); + c2val = (bits & 0xff); + PEEK_BITS(8, bits); + EAT_BITS(8); + c1val = (bits & 0xff); + + PUT_PIXEL_PAIR; + + for (x = 2; x < width ; x += 2) { + /* The compression reversed the even and odd columns.*/ + PARSE_PIXEL(c2val); + PARSE_PIXEL(c1val); + PUT_PIXEL_PAIR; + } + } +} diff --git a/libv4lconvert/sn9c20x.c b/libv4lconvert/sn9c20x.c new file mode 100644 index 0000000..b09483c --- /dev/null +++ b/libv4lconvert/sn9c20x.c @@ -0,0 +1,128 @@ +/* + * Sonix SN9C20X decoder + * Vasily Khoruzhick, (C) 2008-2009 + * Algorithm based on Java code written by Jens on microdia google group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + * + * Note this code was originally licensed under the GNU GPL instead of the + * GNU LGPL, its license has been changed by its author. + */ + +#include "libv4lconvert-priv.h" + +#define DO_SANITY_CHECKS 0 + +static const int Y_coords_624x[128][2] = { + { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0}, { 4, 0}, { 5, 0}, { 6, 0}, { 7, 0}, + { 0, 1}, { 1, 1}, { 2, 1}, { 3, 1}, { 4, 1}, { 5, 1}, { 6, 1}, { 7, 1}, + { 0, 2}, { 1, 2}, { 2, 2}, { 3, 2}, { 4, 2}, { 5, 2}, { 6, 2}, { 7, 2}, + { 0, 3}, { 1, 3}, { 2, 3}, { 3, 3}, { 4, 3}, { 5, 3}, { 6, 3}, { 7, 3}, + + { 0, 4}, { 1, 4}, { 2, 4}, { 3, 4}, { 4, 4}, { 5, 4}, { 6, 4}, { 7, 4}, + { 0, 5}, { 1, 5}, { 2, 5}, { 3, 5}, { 4, 5}, { 5, 5}, { 6, 5}, { 7, 5}, + { 0, 6}, { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6}, { 7, 6}, + { 0, 7}, { 1, 7}, { 2, 7}, { 3, 7}, { 4, 7}, { 5, 7}, { 6, 7}, { 7, 7}, + + { 8, 0}, { 9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}, + { 8, 1}, { 9, 1}, {10, 1}, {11, 1}, {12, 1}, {13, 1}, {14, 1}, {15, 1}, + { 8, 2}, { 9, 2}, {10, 2}, {11, 2}, {12, 2}, {13, 2}, {14, 2}, {15, 2}, + { 8, 3}, { 9, 3}, {10, 3}, {11, 3}, {12, 3}, {13, 3}, {14, 3}, {15, 3}, + + { 8, 4}, { 9, 4}, {10, 4}, {11, 4}, {12, 4}, {13, 4}, {14, 4}, {15, 4}, + { 8, 5}, { 9, 5}, {10, 5}, {11, 5}, {12, 5}, {13, 5}, {14, 5}, {15, 5}, + { 8, 6}, { 9, 6}, {10, 6}, {11, 6}, {12, 6}, {13, 6}, {14, 6}, {15, 6}, + { 8, 7}, { 9, 7}, {10, 7}, {11, 7}, {12, 7}, {13, 7}, {14, 7}, {15, 7} +}; + +static void do_write_u(const unsigned char *buf, unsigned char *ptr, + int i, int j) +{ + *ptr = buf[i + 128 + j]; +} + +static void do_write_v(const unsigned char *buf, unsigned char *ptr, + int i, int j) +{ + *ptr = buf[i + 160 + j]; +} + +void v4lconvert_sn9c20x_to_yuv420(const unsigned char *raw, unsigned char *i420, + int width, int height, int yvu) +{ + int i = 0, x = 0, y = 0, j, relX, relY, x_div2, y_div2; + const unsigned char *buf = raw; + unsigned char *ptr; + int frame_size = width * height; + int frame_size_div2 = frame_size >> 1; + int frame_size_div4 = frame_size >> 2; + int width_div2 = width >> 1; +#if (DO_SANITY_CHECKS == 1) + int height_div2 = height >> 1; +#endif + void (*do_write_uv1)(const unsigned char *buf, unsigned char *ptr, int i, + int j) = NULL; + void (*do_write_uv2)(const unsigned char *buf, unsigned char *ptr, int i, + int j) = NULL; + + if (yvu) { + do_write_uv1 = do_write_v; + do_write_uv2 = do_write_u; + } else { + do_write_uv1 = do_write_u; + do_write_uv2 = do_write_v; + } + + while (i < (frame_size + frame_size_div2)) { + for (j = 0; j < 128; j++) { + relX = x + Y_coords_624x[j][0]; + relY = y + Y_coords_624x[j][1]; + +#if (DO_SANITY_CHECKS == 1) + if ((relX < width) && (relY < height)) { +#endif + ptr = i420 + relY * width + relX; + *ptr = buf[i + j]; +#if (DO_SANITY_CHECKS == 1) + } +#endif + + } + x_div2 = x >> 1; + y_div2 = y >> 1; + for (j = 0; j < 32; j++) { + relX = (x_div2) + (j & 0x07); + relY = (y_div2) + (j >> 3); + +#if (DO_SANITY_CHECKS == 1) + if ((relX < width_div2) && (relY < height_div2)) { +#endif + ptr = i420 + frame_size + + relY * width_div2 + relX; + do_write_uv1(buf, ptr, i, j); + ptr += frame_size_div4; + do_write_uv2(buf, ptr, i, j); +#if (DO_SANITY_CHECKS == 1) + } +#endif + } + + i += 192; + x += 16; + if (x >= width) { + x = 0; + y += 8; + } + } +} diff --git a/libv4lconvert/spca501.c b/libv4lconvert/spca501.c new file mode 100644 index 0000000..621ebb8 --- /dev/null +++ b/libv4lconvert/spca501.c @@ -0,0 +1,250 @@ +/* +# (C) 2008 Hans de Goede + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include "libv4lconvert-priv.h" + +/* YUYV per line */ +void v4lconvert_spca501_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu) +{ + int i, j; + unsigned long *lsrc = (unsigned long *)src; + + for (i = 0; i < height; i += 2) { + /* -128 - 127 --> 0 - 255 and copy first line Y */ + unsigned long *ldst = (unsigned long *)(dst + i * width); + + for (j = 0; j < width; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line U */ + if (yvu) + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + else + ldst = (unsigned long *)(dst + width * height + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy second line Y */ + ldst = (unsigned long *)(dst + i * width + width); + for (j = 0; j < width; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line V */ + if (yvu) + ldst = (unsigned long *)(dst + width * height + i * width / 4); + else + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + } +} + +/* YYUV per line */ +void v4lconvert_spca505_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu) +{ + int i, j; + unsigned long *lsrc = (unsigned long *)src; + + for (i = 0; i < height; i += 2) { + /* -128 - 127 --> 0 - 255 and copy 2 lines of Y */ + unsigned long *ldst = (unsigned long *)(dst + i * width); + + for (j = 0; j < width*2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line U */ + if (yvu) + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + else + ldst = (unsigned long *)(dst + width * height + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line V */ + if (yvu) + ldst = (unsigned long *)(dst + width * height + i * width / 4); + else + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + } +} + +/* YUVY per line */ +void v4lconvert_spca508_to_yuv420(const unsigned char *src, unsigned char *dst, + int width, int height, int yvu) +{ + int i, j; + unsigned long *lsrc = (unsigned long *)src; + + for (i = 0; i < height; i += 2) { + /* -128 - 127 --> 0 - 255 and copy first line Y */ + unsigned long *ldst = (unsigned long *)(dst + i * width); + for (j = 0; j < width; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line U */ + if (yvu) + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + else + ldst = (unsigned long *)(dst + width * height + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy 1 line V */ + if (yvu) + ldst = (unsigned long *)(dst + width * height + i * width / 4); + else + ldst = (unsigned long *)(dst + (width * height * 5) / 4 + i * width / 4); + for (j = 0; j < width/2; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + + /* -128 - 127 --> 0 - 255 and copy second line Y */ + ldst = (unsigned long *)(dst + i * width + width); + for (j = 0; j < width; j += sizeof(long)) { + *ldst = *lsrc++; + *ldst++ ^= 0x8080808080808080ULL; + } + } +} + +/* Note this is not a spca specific format, bit it fits in this file in that + it is another funny yuv format */ +/* one line of Y then 1 line of VYUY */ +void v4lconvert_cit_yyvyuy_to_yuv420(const unsigned char *src, + unsigned char *ydest, + int width, int height, int yvu) +{ + int x, y; + unsigned char *udest, *vdest; + + if (yvu) { + vdest = ydest + width * height; + udest = vdest + (width * height) / 4; + } else { + udest = ydest + width * height; + vdest = udest + (width * height) / 4; + } + + for (y = 0; y < height; y += 2) { + /* copy 1 line of Y */ + memcpy(ydest, src, width); + src += width; + ydest += width; + + /* Split one line of VYUY */ + for (x = 0; x < width; x += 2) { + *vdest++ = *src++; + *ydest++ = *src++; + *udest++ = *src++; + *ydest++ = *src++; + } + } +} + +/* Note this is not a spca specific format, but it fits in this file in that + it is another funny yuv format */ +/* The konica gspca subdriver using cams send data in blocks of 256 pixels + in YUV420 format. */ +void v4lconvert_konica_yuv420_to_yuv420(const unsigned char *src, + unsigned char *ydest, + int width, int height, int yvu) +{ + int i, no_blocks; + unsigned char *udest, *vdest; + + if (yvu) { + vdest = ydest + width * height; + udest = vdest + (width * height) / 4; + } else { + udest = ydest + width * height; + vdest = udest + (width * height) / 4; + } + + no_blocks = width * height / 256; + for (i = 0; i < no_blocks; i++) { + /* copy 256 Y pixels */ + memcpy(ydest, src, 256); + src += 256; + ydest += 256; + + /* copy 64 U pixels */ + memcpy(udest, src, 64); + src += 64; + udest += 64; + + /* copy 64 V pixels */ + memcpy(vdest, src, 64); + src += 64; + vdest += 64; + } +} + +/* And another not a spca specific format, but fitting in this file in that + it is another funny yuv format */ +/* Two lines of Y then 1 line of UV */ +void v4lconvert_m420_to_yuv420(const unsigned char *src, + unsigned char *ydest, + int width, int height, int yvu) +{ + int x, y; + unsigned char *udest, *vdest; + + if (yvu) { + vdest = ydest + width * height; + udest = vdest + (width * height) / 4; + } else { + udest = ydest + width * height; + vdest = udest + (width * height) / 4; + } + + for (y = 0; y < height; y += 2) { + /* copy 2 lines of Y */ + memcpy(ydest, src, 2 * width); + src += 2 * width; + ydest += 2 * width; + + /* Split one line of UV */ + for (x = 0; x < width; x += 2) { + *udest++ = *src++; + *vdest++ = *src++; + } + } +} diff --git a/libv4lconvert/spca561-decompress.c b/libv4lconvert/spca561-decompress.c new file mode 100644 index 0000000..cb14a06 --- /dev/null +++ b/libv4lconvert/spca561-decompress.c @@ -0,0 +1,1006 @@ +/* + +# Spca561decoder (C) 2005 Andrzej Szombierski [qq@kuku.eu.org] + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License, version 2.1, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + +# Note this code was originally licensed under the GNU GPL instead of the +# GNU LGPL, its license has been changed with permission, see the permission +# mail at the end of this file. + + */ + +/* + * Decoder for compressed spca561 images + * It was developed for "Labtec WebCam Elch 2(SPCA561A)" (046d:0929) + * but it might work with other spca561 cameras + */ +#include +#include "libv4lconvert-priv.h" + +/*fixme: not reentrant */ +static unsigned int bit_bucket; +static const unsigned char *input_ptr; + +static inline void refill(int *bitfill) +{ + if (*bitfill < 8) { + bit_bucket = (bit_bucket << 8) | *(input_ptr++); + *bitfill += 8; + } +} + +static inline int nbits(int *bitfill, int n) +{ + bit_bucket = (bit_bucket << 8) | *(input_ptr++); + *bitfill -= n; + return (bit_bucket >> (*bitfill & 0xff)) & ((1 << n) - 1); +} + +static inline int _nbits(int *bitfill, int n) +{ + *bitfill -= n; + return (bit_bucket >> (*bitfill & 0xff)) & ((1 << n) - 1); +} + +static int fun_A(int *bitfill) +{ + int ret; + static int tab[] = { + 12, 13, 14, 15, 16, 17, 18, 19, -12, -13, -14, -15, + -16, -17, -18, -19, -19 + }; + + ret = tab[nbits(bitfill, 4)]; + + refill(bitfill); + return ret; +} + +static int fun_B(int *bitfill) +{ + static int tab1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 16, 17, + 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 + }; + static int tab[] = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, -5, + -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, + -18, -19 + }; + unsigned int tmp; + + tmp = nbits(bitfill, 7) - 68; + refill(bitfill); + if (tmp > 47) + return 0xff; + return tab[tab1[tmp]]; +} + +static int fun_C(int *bitfill, int gkw) +{ + static int tab1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 12, 13, + 14, + 15, 16, 17, 18, 19, 20, 21, 22 + }; + static int tab[] = { + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, -9, -10, -11, + -12, -13, -14, -15, -16, -17, -18, -19 + }; + unsigned int tmp; + + if (gkw == 0xfe) { + if (nbits(bitfill, 1) == 0) + return 7; + else + return -8; + } + + if (gkw != 0xff) + return 0xff; + + tmp = nbits(bitfill, 7) - 72; + if (tmp > 43) + return 0xff; + + refill(bitfill); + return tab[tab1[tmp]]; +} + +static int fun_D(int *bitfill, int gkw) +{ + if (gkw == 0xfd) { + if (nbits(bitfill, 1) == 0) + return 12; + return -13; + } + + if (gkw == 0xfc) { + if (nbits(bitfill, 1) == 0) + return 13; + return -14; + } + + if (gkw == 0xfe) { + switch (nbits(bitfill, 2)) { + case 0: + return 14; + case 1: + return -15; + case 2: + return 15; + case 3: + return -16; + } + } + + if (gkw == 0xff) { + switch (nbits(bitfill, 3)) { + case 4: + return 16; + case 5: + return -17; + case 6: + return 17; + case 7: + return -18; + case 2: + return _nbits(bitfill, 1) ? 0xed : 0x12; + case 3: + (*bitfill)--; + return 18; + } + return 0xff; + } + return gkw; +} + +static int fun_E(int cur_byte, int *bitfill) +{ + static int tab0[] = { 0, -1, 1, -2, 2, -3, 3, -4 }; + static int tab1[] = { 4, -5, 5, -6, 6, -7, 7, -8 }; + static int tab2[] = { 8, -9, 9, -10, 10, -11, 11, -12 }; + static int tab3[] = { 12, -13, 13, -14, 14, -15, 15, -16 }; + static int tab4[] = { 16, -17, 17, -18, 18, -19, 19, -19 }; + + if ((cur_byte & 0xf0) >= 0x80) { + *bitfill -= 4; + return tab0[(cur_byte >> 4) & 7]; + } + if ((cur_byte & 0xc0) == 0x40) { + *bitfill -= 5; + return tab1[(cur_byte >> 3) & 7]; + + } + if ((cur_byte & 0xe0) == 0x20) { + *bitfill -= 6; + return tab2[(cur_byte >> 2) & 7]; + + } + if ((cur_byte & 0xf0) == 0x10) { + *bitfill -= 7; + return tab3[(cur_byte >> 1) & 7]; + + } + if ((cur_byte & 0xf8) == 8) { + *bitfill -= 8; + return tab4[cur_byte & 7]; + } + return 0xff; +} + +static int fun_F(int cur_byte, int *bitfill) +{ + *bitfill -= 5; + switch (cur_byte & 0xf8) { + case 0x80: + return 0; + case 0x88: + return -1; + case 0x90: + return 1; + case 0x98: + return -2; + case 0xa0: + return 2; + case 0xa8: + return -3; + case 0xb0: + return 3; + case 0xb8: + return -4; + case 0xc0: + return 4; + case 0xc8: + return -5; + case 0xd0: + return 5; + case 0xd8: + return -6; + case 0xe0: + return 6; + case 0xe8: + return -7; + case 0xf0: + return 7; + case 0xf8: + return -8; + } + + *bitfill -= 1; + switch (cur_byte & 0xfc) { + case 0x40: + return 8; + case 0x44: + return -9; + case 0x48: + return 9; + case 0x4c: + return -10; + case 0x50: + return 10; + case 0x54: + return -11; + case 0x58: + return 11; + case 0x5c: + return -12; + case 0x60: + return 12; + case 0x64: + return -13; + case 0x68: + return 13; + case 0x6c: + return -14; + case 0x70: + return 14; + case 0x74: + return -15; + case 0x78: + return 15; + case 0x7c: + return -16; + } + + *bitfill -= 1; + switch (cur_byte & 0xfe) { + case 0x20: + return 16; + case 0x22: + return -17; + case 0x24: + return 17; + case 0x26: + return -18; + case 0x28: + return 18; + case 0x2a: + return -19; + case 0x2c: + return 19; + } + + *bitfill += 7; + return 0xff; +} + +static int internal_spca561_decode(int width, int height, + const unsigned char *inbuf, + unsigned char *outbuf) +{ + /* buffers */ + static int accum[8 * 8 * 8]; + static int i_hits[8 * 8 * 8]; + + static const int nbits_A[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, + 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + }; + static const int tab_A[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 11, -11, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, + 10, 10, + 255, 254, -4, + -4, -5, -5, -6, -6, -7, -7, -8, -8, -9, -9, -10, -10, -1, + -1, -1, + -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, + 3, 3, 3, + -2, -2, -2, -2, -2, -2, -2, -2, -3, -3, -3, -3, -3, -3, -3, + -3, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, + 1 + }; + + static const int nbits_B[] = { + 0, 8, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }; + static const int tab_B[] = { + 0xff, -4, 3, 3, -3, -3, -3, -3, 2, 2, 2, 2, 2, 2, 2, 2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + }; + + static const int nbits_C[] = { + 0, 0, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, + 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + }; + static const int tab_C[] = { + 0xff, 0xfe, 6, -7, 5, 5, -6, -6, 4, 4, 4, 4, -5, -5, -5, -5, + 3, 3, 3, 3, 3, 3, 3, 3, -4, -4, -4, -4, -4, -4, -4, -4, 2, + 2, 2, 2, + 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, -3, -3, -3, -3, -3, -3, -3, -3, + -3, -3, + -3, -3, -3, + -3, -3, -3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, + -2, -2, + -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, + -2, -2, + -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, + }; + + static const int nbits_D[] = { + 0, 0, 0, 0, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, + 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, + 4, 4, + 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, + 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, + 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, + 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 + }; + static const int tab_D[] = { + 0xff, 0xfe, 0xfd, 0xfc, 10, -11, 11, -12, 8, 8, -9, -9, 9, 9, + -10, -10, 6, 6, 6, 6, -7, -7, -7, -7, 7, 7, 7, 7, -8, -8, + -8, -8, + 4, 4, 4, 4, + 4, 4, 4, 4, -5, -5, -5, -5, -5, -5, -5, -5, 5, 5, 5, 5, 5, + 5, 5, 5, + -6, -6, + -6, -6, -6, -6, -6, -6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, + 2, 2, -3, + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, + 3, 3, + 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, -4, -4, -4, -4, -4, -4, -4, + -4, -4, + -4, -4, -4, + -4, -4, -4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, + -1, -1, + -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, + -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, + -2, -2 + }; + + /* a_curve[19 + i] = ... [-19..19] => [-160..160] */ + static const int a_curve[] = { + -160, -144, -128, -112, -98, -88, -80, -72, -64, -56, -48, + -40, -32, -24, -18, -12, -8, -5, -2, 0, 2, 5, 8, 12, 18, + 24, 32, + 40, 48, 56, 64, + 72, 80, 88, 98, 112, 128, 144, 160 + }; + /* clamp0_255[256 + i] = min(max(i,255),0) */ + static const unsigned char clamp0_255[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, + 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, + 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, + 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, + 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, + 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, + 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, + 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, + 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, + 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, + 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, + 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, + 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, + 255 + }; + /* abs_clamp15[19 + i] = min(abs(i), 15) */ + static const int abs_clamp15[] = { + 15, 15, 15, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, + 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 15, 15, + 15 + }; + /* diff_encoding[256 + i] = ... */ + static const int diff_encoding[] = { + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, + 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, + 5, 5, + 5, 5, 5, 5, 5, 3, 3, + 3, 3, 1, 1, 0, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, + 6, 6, 6, 6, 6, 6 + }; + + int block; + int bitfill = 0; + int xwidth = width + 6; + int off_up_right = 2 - 2 * xwidth; + int off_up_left = -2 - 2 * xwidth; + int pixel_U = 0, saved_pixel_UR = 0; + int pixel_x = 0, pixel_y = 2; + unsigned char *output_ptr = outbuf; + + memset(i_hits, 0, sizeof(i_hits)); + memset(accum, 0, sizeof(accum)); + + memcpy(outbuf + xwidth * 2 + 3, inbuf + 0x14, width); + memcpy(outbuf + xwidth * 3 + 3, inbuf + 0x14 + width, width); + + input_ptr = inbuf + 0x14 + width * 2; + output_ptr = outbuf + (xwidth) * 4 + 3; + + bit_bucket = 0; + + for (block = 0; block < ((height - 2) * width) / 32; ++block) { + int b_it, var_7 = 0; + int cur_byte; + + refill(&bitfill); + + cur_byte = (bit_bucket >> (bitfill & 7)) & 0xff; + + if ((cur_byte & 0x80) == 0) { + var_7 = 0; + bitfill--; + } else if ((cur_byte & 0xC0) == 0x80) { + var_7 = 1; + bitfill -= 2; + } else if ((cur_byte & 0xc0) == 0xc0) { + var_7 = 2; + bitfill -= 2; + } + + for (b_it = 0; b_it < 32; b_it++) { + int index; + int pixel_L, pixel_UR, pixel_UL; + int multiplier; + int dL, dC, dR; + int gkw; /* God knows what */ + + refill(&bitfill); + cur_byte = bit_bucket >> (bitfill & 7) & 0xff; + + pixel_L = output_ptr[-2]; + pixel_UR = output_ptr[off_up_right]; + pixel_UL = output_ptr[off_up_left]; + + dL = diff_encoding[0x100 + pixel_UL - pixel_L]; + dC = diff_encoding[0x100 + pixel_U - pixel_UL]; + dR = diff_encoding[0x100 + pixel_UR - pixel_U]; + + if (pixel_x < 2) { + pixel_L = pixel_UL = pixel_U = + output_ptr[-xwidth * 2]; + pixel_UR = output_ptr[off_up_right]; + dL = dC = 0; + dR = diff_encoding[0x100 + pixel_UR - + pixel_U]; + } else if (pixel_x > width - 3) + dR = 0; + + multiplier = 4; + index = dR + dC * 8 + dL * 64; + + if (pixel_L + pixel_U * 2 <= 144 + && (pixel_y & 1) == 0 + && (b_it & 3) == 0 && (dR < 5) && (dC < 5) + && (dL < 5)) { + multiplier = 1; + } else if (pixel_L <= 48 + && dL <= 4 && dC <= 4 && dL >= 1 + && dC >= 1) { + multiplier = 2; + } else if (var_7 == 1) { + multiplier = 2; + } else if (dC + dL >= 11 || var_7 == 2) { + multiplier = 8; + } + + if (i_hits[index] < 7) { + bitfill -= nbits_A[cur_byte]; + gkw = tab_A[cur_byte]; + if (gkw == 0xfe) + gkw = fun_A(&bitfill); + } else if (i_hits[index] >= accum[index]) { + bitfill -= nbits_B[cur_byte]; + gkw = tab_B[cur_byte]; + if (cur_byte == 0) + gkw = fun_B(&bitfill); + } else if (i_hits[index] * 2 >= accum[index]) { + bitfill -= nbits_C[cur_byte]; + gkw = tab_C[cur_byte]; + if (cur_byte < 2) + gkw = fun_C(&bitfill, gkw); + } else if (i_hits[index] * 4 >= accum[index]) { + bitfill -= nbits_D[cur_byte]; + gkw = tab_D[cur_byte]; + if (cur_byte < 4) + gkw = fun_D(&bitfill, gkw); + } else if (i_hits[index] * 8 >= accum[index]) { + gkw = fun_E(cur_byte, &bitfill); + } else { + gkw = fun_F(cur_byte, &bitfill); + } + + if (gkw == 0xff) + return -3; + + { + int tmp1, tmp2; + + tmp1 = + (pixel_U + pixel_L) * 3 - pixel_UL * 2; + tmp1 += (tmp1 < 0) ? 3 : 0; + tmp2 = a_curve[19 + gkw] * multiplier; + tmp2 += (tmp2 < 0) ? 1 : 0; + + *(output_ptr++) = + clamp0_255[0x100 + (tmp1 >> 2) - + (tmp2 >> 1)]; + } + pixel_U = saved_pixel_UR; + saved_pixel_UR = pixel_UR; + + if (++pixel_x == width) { + output_ptr += 6; + pixel_x = 0; + pixel_y++; + } + + accum[index] += abs_clamp15[19 + gkw]; + + if (i_hits[index]++ == 15) { + i_hits[index] = 8; + accum[index] /= 2; + } + } + } + return 0; +} + +/* FIXME, change internal_spca561_decode not to need the extra border + around its dest buffer */ +void v4lconvert_decode_spca561(const unsigned char *inbuf, + unsigned char *outbuf, int width, int height) +{ + int i; + static unsigned char tmpbuf[650 * 490]; + + if (internal_spca561_decode(width, height, inbuf, tmpbuf) != 0) + return; + for (i = 0; i < height; i++) + memcpy(outbuf + i * width, + tmpbuf + (i + 2) * (width + 6) + 3, width); +} + +/*************** License Change Permission Notice *************** + + Return-Path: +Received: from koko.hhs.nl ([145.52.2.16] verified) +by hhs.nl (CommuniGate Pro SMTP 4.3.6) +with ESMTP id 88574071 for j.w.r.degoede@hhs.nl; Mon, 16 Jun 2008 16:36:24 +0200 +Received: from exim (helo=koko) +by koko.hhs.nl with local-smtp (Exim 4.62) +(envelope-from ) +id 1K8Fom-0002iJ-3K +for j.w.r.degoede@hhs.nl; Mon, 16 Jun 2008 16:36:24 +0200 +Received: from [192.87.102.74] (port=41377 helo=filter6-ams.mf.surf.net) +by koko.hhs.nl with esmtp (Exim 4.62) +(envelope-from ) +id 1K8Fol-0002iC-Qo +for j.w.r.degoede@hhs.nl; Mon, 16 Jun 2008 16:36:23 +0200 +Received: from kuku.eu.org (pa90.wielkopole.sdi.tpnet.pl [217.99.123.90]) +by filter6-ams.mf.surf.net (8.13.8/8.13.8/Debian-3) with ESMTP id m5GEa55r001787 +for ; Mon, 16 Jun 2008 16:36:06 +0200 +Received: (qmail 2243 invoked by uid 500); 16 Jun 2008 14:29:37 -0000 +Date: Mon, 16 Jun 2008 16:29:37 +0200 (CEST) +From: Andrzej Szombierski +To: Hans de Goede +Subject: Re: spca561 decoder license question +In-Reply-To: <485673B6.4050003@hhs.nl> +Message-ID: +References: <485673B6.4050003@hhs.nl> +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=iso-8859-2 +Content-Transfer-Encoding: QUOTED-PRINTABLE +X-Canit-CHI2: 0.00 +X-Bayes-Prob: 0.0001 (Score 0, tokens from: @@RPTN) +X-Spam-Score: 2.00 (**) [Tag at 6.00] RBL(uceprotect-blacklist.surfnet.nl,2.0) +X-CanItPRO-Stream: hhs:j.w.r.degoede@hhs.nl (inherits from hhs:default,base:default) +X-Canit-Stats-ID: 85673281 - 37e52c8b07bc +X-Scanned-By: CanIt (www . roaringpenguin . com) on 192.87.102.74 +X-Anti-Virus: Kaspersky Anti-Virus for MailServers 5.5.2/RELEASE, bases: 16062008 #776409, status: clean + +On Mon, 16 Jun 2008, Hans de Goede wrote: + +> Hi, +>=20 +> I don't know if you're still subscribed to the spca devel mailing list, s= +o let=20 +> me start with a short intro. +> +> I'm a Linux enthusiast / developer currently helping Jean-Fran=E7ois Moin= +e with=20 +> porting gspca to video4linux2 and cleaning up the code to get it ready fo= +r=20 +> mainline kernel inclusion. +>=20 +> As part of this process the decompression code for all supported cams mus= +t be=20 +> moved to userspace, as doing in kernel decompression is considered unwant= +ed by=20 +> the mainline people (I agree) as it should be done in userspace. +> + +Sounds reasonable. +=20 +> As such I'm working on a library which does decompression of custom cam f= +ormats=20 +> in userspace. +> + +Nice. I hope that the library won't be limited to spca-supported webcams,= +=20 +and as an application developer I would be able to just request RGB data=20 +from any /dev/video*, right? + +> I do not want to license this library as GPL (as the current spca code is= + ), as=20 +> it should be usable by as much software as possible. Instead I want to li= +cense=20 +> it under the LGPL version 2.1 or later. + +Also sounds reasonable. + +>=20 +> So my question us my I have your permission to relicense your spca561=20 +> decompression code under the LGPL? +>=20 + +Yes, of course.=20 + +> Thanks & Regards, + >=20 + > Hans + >=20 + > + + --=20 + :: Andrzej Szombierski :: qq@kuku.eu.org :: http://kuku.eu.org :: + :: anszom@bezkitu.com :: radio bez kitu :: http://bezkitu.com :: + + */ diff --git a/libv4lconvert/sq905c.c b/libv4lconvert/sq905c.c new file mode 100644 index 0000000..12b5acc --- /dev/null +++ b/libv4lconvert/sq905c.c @@ -0,0 +1,217 @@ +/* + * sq905c.c + * + * Here is the decompression function for the SQ905C cameras. The functions + * used are adapted from the libgphoto2 functions for the same cameras, + * which was + * Copyright (c) 2005 and 2007 Theodore Kilgore + * This version for libv4lconvert is + * Copyright (c) 2009 Theodore Kilgore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include + +#include "libv4lconvert-priv.h" + + +#define CLIP(x) ((x) < 0 ? 0 : ((x) > 0xff) ? 0xff : (x)) + + + static int +sq905c_first_decompress(unsigned char *output, const unsigned char *input, + unsigned int outputsize) +{ + unsigned char parity = 0; + unsigned char nibble_to_keep[2]; + unsigned char temp1 = 0, temp2 = 0; + unsigned char input_byte; + unsigned char lookup = 0; + unsigned int i = 0; + unsigned int bytes_used = 0; + unsigned int bytes_done = 0; + unsigned int bit_counter = 8; + unsigned int cycles = 0; + int table[9] = { -1, 0, 2, 6, 0x0e, 0x0e, 0x0e, 0x0e, 0xfb}; + unsigned char lookup_table[16] = { + 0, 2, 6, 0x0e, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb + }; + unsigned char translator[16] = { + 8, 7, 9, 6, 10, 11, 12, 13, + 14, 15, 5, 4, 3, 2, 1, 0 + }; + + nibble_to_keep[0] = 0; + nibble_to_keep[1] = 0; + + while (bytes_done < outputsize) { + while (parity < 2) { + while (lookup > table[cycles]) { + if (bit_counter == 8) { + input_byte = input[bytes_used]; + bytes_used++; + temp1 = input_byte; + bit_counter = 0; + } + input_byte = temp1; + temp2 = (temp2 << 1) & 0xFF; + input_byte = input_byte >> 7; + temp2 = temp2 | input_byte; + temp1 = (temp1 << 1) & 0xFF; + bit_counter++; + cycles++; + if (cycles > 8) + return -1; + lookup = temp2 & 0xff; + } + temp2 = 0; + for (i = 0; i < 17; i++) { + if (i == 16) + return -1; + if (lookup == lookup_table[i]) { + nibble_to_keep[parity] = translator[i]; + break; + } + } + cycles = 0; + parity++; + } + output[bytes_done] = (nibble_to_keep[0]<<4)|nibble_to_keep[1]; + bytes_done++; + parity = 0; + } + return 0; +} + + static int +sq905c_second_decompress(unsigned char *uncomp, unsigned char *in, + int width, int height) +{ + int diff = 0; + int tempval = 0; + int i, m; + unsigned char delta_left = 0; + unsigned char delta_right = 0; + int input_counter = 0; + int delta_table[] = { + -144, -110, -77, -53, -35, -21, -11, -3, + 2, 10, 20, 34, 52, 76, 110, 144 + }; + unsigned char *templine_red; + unsigned char *templine_green; + unsigned char *templine_blue; + + templine_red = malloc(width); + if (!templine_red) { + return -1; + } + for (i = 0; i < width; i++) + templine_red[i] = 0x80; + templine_green = malloc(width); + if (!templine_green) { + free(templine_red); + return -1; + } + for (i = 0; i < width; i++) + templine_green[i] = 0x80; + templine_blue = malloc(width); + if (!templine_blue) { + free(templine_red); + free(templine_green); + return -1; + } + for (i = 0; i < width; i++) + templine_blue[i] = 0x80; + for (m = 0; m < height / 2; m++) { + /* First we do an even-numbered line */ + for (i = 0; i < width / 2; i++) { + delta_right = in[input_counter] & 0x0f; + delta_left = (in[input_counter] >> 4) & 0xff; + input_counter++; + /* left pixel (red) */ + diff = delta_table[delta_left]; + if (!i) + tempval = templine_red[0] + diff; + else + tempval = (templine_red[i] + + uncomp[2 * m * width + 2 * i - 2]) / 2 + diff; + tempval = CLIP(tempval); + uncomp[2 * m * width + 2 * i] = tempval; + templine_red[i] = tempval; + /* right pixel (green) */ + diff = delta_table[delta_right]; + if (!i) + tempval = templine_green[1] + diff; + else if (2 * i == width - 2) + tempval = (templine_green[i] + + uncomp[2 * m * width + 2 * i - 1]) / 2 + diff; + else + tempval = (templine_green[i + 1] + + uncomp[2 * m * width + 2 * i - 1]) / 2 + diff; + tempval = CLIP(tempval); + uncomp[2 * m * width + 2 * i + 1] = tempval; + templine_green[i] = tempval; + } + /* then an odd-numbered line */ + for (i = 0; i < width/2; i++) { + delta_right = in[input_counter] & 0x0f; + delta_left = (in[input_counter] >> 4) & 0xff; + input_counter++; + /* left pixel (green) */ + diff = delta_table[delta_left]; + if (!i) + tempval = templine_green[0] + diff; + else + tempval = (templine_green[i] + + uncomp[(2 * m + 1) * width + 2 * i - 2]) / 2 + diff; + tempval = CLIP(tempval); + uncomp[(2*m+1)*width+2*i] = tempval; + templine_green[i] = tempval; + /* right pixel (blue) */ + diff = delta_table[delta_right]; + if (!i) + tempval = templine_blue[0] + diff; + else + tempval = (templine_blue[i] + + uncomp[(2 * m + 1) * width + 2 * i - 1]) / 2 + diff; + tempval = CLIP(tempval); + uncomp[(2 * m + 1) * width + 2 * i + 1] = tempval; + templine_blue[i] = tempval; + } + } + free(templine_green); + free(templine_red); + free(templine_blue); + return 0; +} + +void v4lconvert_decode_sq905c(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int size; + unsigned char *temp_data; + const unsigned char *raw; + /* here we get rid of the 0x50 bytes of header in src. */ + raw = src + 0x50; + size = width * height / 2; + temp_data = malloc(size); + if (!temp_data) + goto out; + sq905c_first_decompress(temp_data, raw, size); + sq905c_second_decompress(dst, temp_data, width, height); +out: + free(temp_data); +} diff --git a/libv4lconvert/stv0680.c b/libv4lconvert/stv0680.c new file mode 100644 index 0000000..5a6272e --- /dev/null +++ b/libv4lconvert/stv0680.c @@ -0,0 +1,40 @@ +/* + * stv0680.c + * + * Copyright (c) 2009 Hans de Goede + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + */ + +#include "libv4lconvert-priv.h" + +/* The stv0640 first sends all the red/green pixels for a line (so 1/2 width) + and then all the green/blue pixels in that line, shuffle this to a regular + RGGB bayer pattern. */ +void v4lconvert_decode_stv0680(const unsigned char *src, unsigned char *dst, + int width, int height) +{ + int x, y; + const unsigned char *src1 = src; + const unsigned char *src2 = src + width / 2; + + for (y = 0; y < height; y++) { + for (x = 0; x < width / 2; x++) { + *dst++ = *src1++; + *dst++ = *src2++; + } + src1 += width / 2; + src2 += width / 2; + } +} diff --git a/libv4lconvert/tinyjpeg-internal.h b/libv4lconvert/tinyjpeg-internal.h new file mode 100644 index 0000000..91b24ed --- /dev/null +++ b/libv4lconvert/tinyjpeg-internal.h @@ -0,0 +1,127 @@ +/* + * Small jpeg decoder library (Internal header) + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef __TINYJPEG_INTERNAL_H_ +#define __TINYJPEG_INTERNAL_H_ + +#include + +#define SANITY_CHECK 1 + +struct jdec_private; + +#define HUFFMAN_HASH_NBITS 9 +#define HUFFMAN_HASH_SIZE (1UL< RGB decoding */ + unsigned int tmp_buf_y_size; + uint8_t *tmp_buf[COMPONENTS]; +}; + +#define IDCT tinyjpeg_idct_float +void tinyjpeg_idct_float (struct component *compptr, uint8_t *output_buf, int stride); + +#endif + diff --git a/libv4lconvert/tinyjpeg.c b/libv4lconvert/tinyjpeg.c new file mode 100644 index 0000000..01bd409 --- /dev/null +++ b/libv4lconvert/tinyjpeg.c @@ -0,0 +1,2670 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" +#include "libv4lconvert-priv.h" + +enum std_markers { + DQT = 0xDB, /* Define Quantization Table */ + SOF = 0xC0, /* Start of Frame (size information) */ + DHT = 0xC4, /* Huffman Table */ + SOI = 0xD8, /* Start of Image */ + SOS = 0xDA, /* Start of Scan */ + RST = 0xD0, /* Reset Marker d0 -> .. */ + RST7 = 0xD7, /* Reset Marker .. -> d7 */ + EOI = 0xD9, /* End of Image */ + DRI = 0xDD, /* Define Restart Interval */ + APP0 = 0xE0, +}; + +#define cY 0 +#define cCb 1 +#define cCr 2 + +#define BLACK_Y 0 +#define BLACK_U 127 +#define BLACK_V 127 + +#if DEBUG +#if LOG2FILE + +#define trace(fmt, args...) do { \ + FILE *f = fopen("/tmp/jpeg.log", "a"); \ + fprintf(f, fmt, ## args); \ + fflush(f); \ + fclose(f); \ +} while (0) + +#else + +#define trace(fmt, args...) do { \ + fprintf(stderr, fmt, ## args); \ + fflush(stderr); \ +} while (0) +#endif + +#else +#define trace(fmt, args...) do { } while (0) +#endif + +#define error(fmt, args...) do { \ + snprintf(priv->error_string, sizeof(priv->error_string), fmt, ## args); \ + return -1; \ +} while (0) + + +#if 0 +static char *print_bits(unsigned int value, char *bitstr) +{ + int i, j; + + i = 31; + while (i > 0) { + if (value & (1UL << i)) + break; + i--; + } + j = 0; + while (i >= 0) { + bitstr[j++] = (value & (1UL << i)) ? '1' : '0'; + i--; + } + bitstr[j] = 0; + return bitstr; +} + +static void print_next_16bytes(int offset, const unsigned char *stream) +{ + trace("%4.4x: %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", + offset, + stream[0], stream[1], stream[2], stream[3], + stream[4], stream[5], stream[6], stream[7], + stream[8], stream[9], stream[10], stream[11], + stream[12], stream[13], stream[14], stream[15]); +} + +#endif + + +static const unsigned char zigzag[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +static const unsigned char bits_dc_luminance[17] = { + 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; +static const unsigned char val_dc_luminance[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const unsigned char bits_dc_chrominance[17] = { + 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; +static const unsigned char val_dc_chrominance[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const unsigned char bits_ac_luminance[17] = { + 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; +static const unsigned char val_ac_luminance[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const unsigned char bits_ac_chrominance[17] = { + 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 +}; + +static const unsigned char val_ac_chrominance[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +#if 0 /* unused */ +/* Standard JPEG quantization tables from Annex K of the JPEG standard. + Note unlike in Annex K the entries here are in zigzag order! */ +const unsigned char standard_quantization[][64] = { { + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + }, + { + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, +}; +#endif + +/* + * 4 functions to manage the stream + * + * fill_nbits: put at least nbits in the reservoir of bits. + * But convert any 0xff,0x00 into 0xff + * get_nbits: read nbits from the stream, and put it in result, + * bits is removed from the stream and the reservoir is filled + * automaticaly. The result is signed according to the number of + * bits. + * look_nbits: read nbits from the stream without marking as read. + * skip_nbits: read nbits from the stream but do not return the result. + * + * stream: current pointer in the jpeg data (read bytes per bytes) + * nbits_in_reservoir: number of bits filled into the reservoir + * reservoir: register that contains bits information. Only nbits_in_reservoir + * is valid. + * nbits_in_reservoir + * <-- 17 bits --> + * Ex: 0000 0000 1010 0000 1111 0000 <== reservoir + * ^ + * bit 1 + * To get two bits from this example + * result = (reservoir >> 15) & 3 + * + */ +#define fill_nbits(reservoir, nbits_in_reservoir, stream, nbits_wanted) do { \ + while (nbits_in_reservoir < nbits_wanted) { \ + unsigned char c; \ + if (stream >= priv->stream_end) { \ + snprintf(priv->error_string, sizeof(priv->error_string), \ + "fill_nbits error: need %u more bits\n", \ + nbits_wanted - nbits_in_reservoir); \ + longjmp(priv->jump_state, -EIO); \ + } \ + c = *stream++; \ + reservoir <<= 8; \ + if (c == 0xff && *stream == 0x00) \ + stream++; \ + reservoir |= c; \ + nbits_in_reservoir += 8; \ + } \ +} while (0); + +/* Signed version !!!! */ +#define get_nbits(reservoir, nbits_in_reservoir, stream, nbits_wanted, result) do { \ + fill_nbits(reservoir, nbits_in_reservoir, stream, (nbits_wanted)); \ + result = ((reservoir) >> (nbits_in_reservoir - (nbits_wanted))); \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U << nbits_in_reservoir) - 1); \ + if ((unsigned int)result < (1UL << ((nbits_wanted) - 1))) \ + result += (0xFFFFFFFFUL << (nbits_wanted)) + 1; \ +} while (0); + +#define look_nbits(reservoir, nbits_in_reservoir, stream, nbits_wanted, result) do { \ + fill_nbits(reservoir, nbits_in_reservoir, stream, (nbits_wanted)); \ + result = ((reservoir) >> (nbits_in_reservoir - (nbits_wanted))); \ +} while (0); + +/* To speed up the decoding, we assume that the reservoir have enough bit + * slow version: + * #define skip_nbits(reservoir, nbits_in_reservoir, stream, nbits_wanted) do { \ + * fill_nbits(reservoir, nbits_in_reservoir, stream, (nbits_wanted)); \ + * nbits_in_reservoir -= (nbits_wanted); \ + * reservoir &= ((1U << nbits_in_reservoir) - 1); \ + * } while(0); + */ +#define skip_nbits(reservoir, nbits_in_reservoir, stream, nbits_wanted) do { \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U << nbits_in_reservoir) - 1); \ +} while (0); + +#define be16_to_cpu(x) (((x)[0] << 8) | (x)[1]) + +static void resync(struct jdec_private *priv); + +/** + * Get the next (valid) huffman code in the stream. + * + * To speedup the procedure, we look HUFFMAN_HASH_NBITS bits and the code is + * lower than HUFFMAN_HASH_NBITS we have automaticaly the length of the code + * and the value by using two lookup table. + * Else if the value is not found, just search (linear) into an array for each + * bits is the code is present. + * + * If the code is not present for any reason, -1 is return. + */ +static int get_next_huffman_code(struct jdec_private *priv, struct huffman_table *huffman_table) +{ + int value, hcode; + unsigned int extra_nbits, nbits; + uint16_t *slowtable; + + look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, HUFFMAN_HASH_NBITS, hcode); + value = huffman_table->lookup[hcode]; + if (value >= 0) { + unsigned int code_size = huffman_table->code_size[value]; + + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, code_size); + return value; + } + + /* Decode more bits each time ... */ + for (extra_nbits = 0; extra_nbits < 16 - HUFFMAN_HASH_NBITS; extra_nbits++) { + nbits = HUFFMAN_HASH_NBITS + 1 + extra_nbits; + + look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits, hcode); + slowtable = huffman_table->slowtable[extra_nbits]; + /* Search if the code is in this array */ + while (slowtable[0]) { + if (slowtable[0] == hcode) { + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits); + return slowtable[1]; + } + slowtable += 2; + } + } + snprintf(priv->error_string, sizeof(priv->error_string), + "unknown huffman code: %08x\n", (unsigned int)hcode); + longjmp(priv->jump_state, -EIO); + return 0; +} + +/** + * + * Decode a single block that contains the DCT coefficients. + * The table coefficients is already dezigzaged at the end of the operation. + * + */ +static void process_Huffman_data_unit(struct jdec_private *priv, int component) +{ + unsigned char j; + unsigned int huff_code; + unsigned char size_val, count_0; + + struct component *c = &priv->component_infos[component]; + short int DCT[64]; + + /* Initialize the DCT coef table */ + memset(DCT, 0, sizeof(DCT)); + + /* DC coefficient decoding */ + huff_code = get_next_huffman_code(priv, c->DC_table); + if (huff_code) { + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]); + DCT[0] += c->previous_DC; + c->previous_DC = DCT[0]; + } else { + DCT[0] = c->previous_DC; + } + + + /* AC coefficient decoding */ + j = 1; + while (j < 64) { + huff_code = get_next_huffman_code(priv, c->AC_table); + + size_val = huff_code & 0xF; + count_0 = huff_code >> 4; + + if (size_val == 0) { /* RLE */ + if (count_0 == 0) + break; /* EOB found, go out */ + else if (count_0 == 0xF) + j += 16; /* skip 16 zeros */ + } else { + j += count_0; /* skip count_0 zeroes */ + if (j < 64) { + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } + } + } + + if (j > 64) { + snprintf(priv->error_string, sizeof(priv->error_string), + "error: more then 63 AC components (%d) in huffman unit\n", (int)j); + longjmp(priv->jump_state, -EIO); + } + + for (j = 0; j < 64; j++) + c->DCT[j] = DCT[zigzag[j]]; +} + +/* + * Takes two array of bits, and build the huffman table for size, and code + * + * lookup will return the symbol if the code is less or equal than HUFFMAN_HASH_NBITS. + * code_size will be used to known how many bits this symbol is encoded. + * slowtable will be used when the first lookup didn't give the result. + */ +static int build_huffman_table(struct jdec_private *priv, const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) +{ + unsigned int i, j, code, code_size, val, nbits; + unsigned char huffsize[257], *hz; + unsigned int huffcode[257], *hc; + int slowtable_used[16 - HUFFMAN_HASH_NBITS]; + + /* + * Build a temp array + * huffsize[X] => numbers of bits to write vals[X] + */ + hz = huffsize; + for (i = 1; i <= 16; i++) { + for (j = 1; j <= bits[i]; j++) + *hz++ = i; + } + *hz = 0; + + memset(table->lookup, 0xff, sizeof(table->lookup)); + for (i = 0; i < (16 - HUFFMAN_HASH_NBITS); i++) + slowtable_used[i] = 0; + + /* Build a temp array + * huffcode[X] => code used to write vals[X] + */ + code = 0; + hc = huffcode; + hz = huffsize; + nbits = *hz; + while (*hz) { + while (*hz == nbits) { + *hc++ = code++; + hz++; + } + code <<= 1; + nbits++; + } + + /* + * Build the lookup table, and the slowtable if needed. + */ + for (i = 0; huffsize[i]; i++) { + val = vals[i]; + code = huffcode[i]; + code_size = huffsize[i]; + + trace("val=%2.2x code=%8.8x codesize=%2.2d\n", i, code, code_size); + + table->code_size[val] = code_size; + if (code_size <= HUFFMAN_HASH_NBITS) { + /* + * Good: val can be put in the lookup table, so fill all value of this + * column with value val + */ + int repeat = 1UL << (HUFFMAN_HASH_NBITS - code_size); + + code <<= HUFFMAN_HASH_NBITS - code_size; + while (repeat--) + table->lookup[code++] = val; + + } else { + /* Perhaps sorting the array will be an optimization */ + int slowtable_index = code_size - HUFFMAN_HASH_NBITS - 1; + + if (slowtable_used[slowtable_index] == 254) + error("slow Huffman table overflow\n"); + + table->slowtable[slowtable_index][slowtable_used[slowtable_index]] + = code; + table->slowtable[slowtable_index][slowtable_used[slowtable_index] + 1] + = val; + slowtable_used[slowtable_index] += 2; + } + } + + for (i = 0; i < (16 - HUFFMAN_HASH_NBITS); i++) + table->slowtable[i][slowtable_used[i]] = 0; + + return 0; +} + +static int build_default_huffman_tables(struct jdec_private *priv) +{ + if ((priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE) + && priv->default_huffman_table_initialized) + return 0; + + if (build_huffman_table(priv, bits_dc_luminance, val_dc_luminance, &priv->HTDC[0])) + return -1; + if (build_huffman_table(priv, bits_ac_luminance, val_ac_luminance, &priv->HTAC[0])) + return -1; + + if (build_huffman_table(priv, bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1])) + return -1; + if (build_huffman_table(priv, bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1])) + return -1; + + priv->default_huffman_table_initialized = 1; + return 0; +} + + + +/******************************************************************************* + * + * Colorspace conversion routine + * + * + * Note: + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + ******************************************************************************/ + +static unsigned char clamp(int i) +{ + if (i < 0) + return 0; + if (i > 255) + return 255; + return i; +} + + +/** + * YCrCb -> YUV420P (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_YUV420P_1x1(struct jdec_private *priv) +{ + const unsigned char *s, *y; + unsigned char *p; + int i, j; + + p = priv->plane[0]; + y = priv->Y; + for (i = 0; i < 8; i++) { + memcpy(p, y, 8); + p += priv->width; + y += 8; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i = 0; i < 8; i += 2) { + for (j = 0; j < 8; j += 2, s += 2) + *p++ = *s; + s += 8; /* Skip one line */ + p += priv->width / 2 - 4; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i = 0; i < 8; i += 2) { + for (j = 0; j < 8; j += 2, s += 2) + *p++ = *s; + s += 8; /* Skip one line */ + p += priv->width / 2 - 4; + } +} + +/** + * YCrCb -> YUV420P (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_YUV420P_2x1(struct jdec_private *priv) +{ + unsigned char *p; + const unsigned char *s, *y1; + unsigned int i; + + p = priv->plane[0]; + y1 = priv->Y; + for (i = 0; i < 8; i++) { + memcpy(p, y1, 16); + p += priv->width; + y1 += 16; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i = 0; i < 8; i += 2) { + memcpy(p, s, 8); + s += 16; /* Skip one line */ + p += priv->width / 2; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i = 0; i < 8; i += 2) { + memcpy(p, s, 8); + s += 16; /* Skip one line */ + p += priv->width/2; + } +} + + +/** + * YCrCb -> YUV420P (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_YUV420P_1x2(struct jdec_private *priv) +{ + const unsigned char *s, *y; + unsigned char *p; + int i, j; + + p = priv->plane[0]; + y = priv->Y; + for (i = 0; i < 16; i++) { + memcpy(p, y, 8); + p += priv->width; + y += 8; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j += 2, s += 2) + *p++ = *s; + p += priv->width / 2 - 4; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j += 2, s += 2) + *p++ = *s; + p += priv->width / 2 - 4; + } +} + +/** + * YCrCb -> YUV420P (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_YUV420P_2x2(struct jdec_private *priv) +{ + unsigned char *p; + const unsigned char *s, *y1; + unsigned int i; + + p = priv->plane[0]; + y1 = priv->Y; + for (i = 0; i < 16; i++) { + memcpy(p, y1, 16); + p += priv->width; + y1 += 16; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i = 0; i < 8; i++) { + memcpy(p, s, 8); + s += 8; + p += priv->width / 2; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i = 0; i < 8; i++) { + memcpy(p, s, 8); + s += 8; + p += priv->width / 2; + } +} + +/** + * YCrCb -> RGB24 (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_RGB24_1x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->width * 3 - 8 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + +/** + * YCrCb -> BGR24 (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_BGR24_1x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->width * 3 - 8 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + } + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + + +/** + * YCrCb -> RGB24 (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_RGB24_2x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->width * 3 - 16 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + +/* + * YCrCb -> BGR24 (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_BGR24_2x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->width * 3 - 16 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + +/** + * YCrCb -> RGB24 (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_RGB24_1x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + p2 = priv->plane[0] + priv->width * 3; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2 * priv->width * 3 - 8 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (Y[8-1]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + + } + Y += 8; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + +/* + * YCrCb -> BGR24 (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_BGR24_1x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + p2 = priv->plane[0] + priv->width * 3; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2 * priv->width * 3 - 8 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (Y[8-1]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + + } + Y += 8; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + + +/** + * YCrCb -> RGB24 (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_RGB24_2x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + p2 = priv->plane[0] + priv->width * 3; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = (priv->width * 3 * 2) - 16 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (Y[16-2]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + + y = (Y[16-1]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + } + Y += 16; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + + +/* + * YCrCb -> BGR24 (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_BGR24_2x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i, j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + p = priv->plane[0]; + p2 = priv->plane[0] + priv->width * 3; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = (priv->width * 3 * 2) - 16 * 3; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (Y[16-2]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + + y = (Y[16-1]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + } + Y += 16; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX +} + + + +/** + * YCrCb -> Grey (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_Grey_1x1(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + int offset_to_next_row; + + p = priv->plane[0]; + y = priv->Y; + offset_to_next_row = priv->width; + + for (i = 0; i < 8; i++) { + memcpy(p, y, 8); + y += 8; + p += offset_to_next_row; + } +} + +/** + * YCrCb -> Grey (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_Grey_2x1(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + + p = priv->plane[0]; + y = priv->Y; + + for (i = 0; i < 8; i++) { + memcpy(p, y, 16); + y += 16; + p += priv->width; + } +} + + +/** + * YCrCb -> Grey (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_Grey_1x2(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + + p = priv->plane[0]; + y = priv->Y; + + for (i = 0; i < 16; i++) { + memcpy(p, y, 8); + y += 8; + p += priv->width; + } +} + +/** + * YCrCb -> Grey (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_Grey_2x2(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + + p = priv->plane[0]; + y = priv->Y; + + for (i = 0; i < 16; i++) { + memcpy(p, y, 16); + y += 16; + p += priv->width; + } +} + + +/* + * Decode all the 3 components for 1x1 + */ +static void decode_MCU_1x1_3planes(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + + // Cb + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 1x1 directly in 1 color + */ +static void decode_MCU_1x1_1plane(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + + // Cb + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + + +/* + * Decode a 2x1 + * .-------. + * | 1 | 2 | + * `-------' + */ +static void decode_MCU_2x1_3planes(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 8, 16); + + // Cb + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +static void build_quantization_table(float *qtable, const unsigned char *ref_table); + +static void pixart_decode_MCU_2x1_3planes(struct jdec_private *priv) +{ + unsigned char marker; + + look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, + 8, marker); + + /* Sometimes the pac7302 switches chrominance setting halfway though a + frame, with a quite ugly looking result, so we drop such frames. */ + if (priv->first_marker == 0) + priv->first_marker = marker; + else if ((marker & 0x80) != (priv->first_marker & 0x80)) { + snprintf(priv->error_string, sizeof(priv->error_string), + "Pixart JPEG error: chrominance changed halfway\n"); + longjmp(priv->jump_state, -EIO); + } + + /* Pixart JPEG MCU-s are preceded by a marker indicating the quality + setting with which the MCU is compressed, IOW the MCU-s may have a + different quantization table per MCU. So if the marker changes we + need to rebuild the quantization tables. */ + if (marker != priv->marker) { + int i, j, comp, lumi; + unsigned char qt[64]; + /* These values have been found by trial and error and seem to + work reasonably. Markers with index 0 - 7 are never + generated by the hardware, so they are likely wrong. */ + const int qfactor[32] = { + 25, 30, 35, 40, 45, 50, 55, 60, + 65, 70, 75, 80, 85, 90, 95, 100, + 100, 100, 120, 140, 160, 180, 210, 240, + 270, 300, 330, 360, 390, 420, 450, 480 + }; + /* These tables were found in SPC230NC.SYS */ + const unsigned char pixart_q[][64] = { { + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + }, { + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + }, { + 0x08, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + }, { + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + } }; + + i = (marker & 0x7c) >> 2; /* Bits 0 and 1 are always 0 */ + comp = qfactor[i]; + lumi = (marker & 0x40) ? 1 : 0; + /* printf("marker %02x comp %d lumi %d\n", marker, comp, lumi); */ + + /* Note the DC quantization factor is fixed! */ + qt[0] = pixart_q[lumi][0]; + for (i = 1; i < 64; i++) { + j = (pixart_q[lumi][i] * comp + 50) / 100; + qt[i] = (j < 255) ? j : 255; + } + build_quantization_table(priv->Q_tables[0], qt); + + /* If bit 7 of the marker is set chrominance uses the + luminance quantization table */ + if (!(marker & 0x80)) { + qt[0] = pixart_q[3][0]; + for (i = 1; i < 64; i++) { + j = (pixart_q[3][i] * comp + 50) / 100; + qt[i] = (j < 255) ? j : 255; + } + } + build_quantization_table(priv->Q_tables[1], qt); + + priv->marker = marker; + } + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, 8); + + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 8, 16); + + // Cb + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 2x1 + * .-------. + * | 1 | 2 | + * `-------' + */ +static void decode_MCU_2x1_1plane(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 8, 16); + + // Cb + process_Huffman_data_unit(priv, cCb); + + // Cr + process_Huffman_data_unit(priv, cCr); +} + + +/* + * Decode a 2x2 + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void decode_MCU_2x2_3planes(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 8, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 64 * 2, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 64 * 2 + 8, 16); + + // Cb + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 2x2 directly in GREY format (8bits) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void decode_MCU_2x2_1plane(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 8, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 64 * 2, 16); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 64 * 2 + 8, 16); + + // Cb + process_Huffman_data_unit(priv, cCb); + + // Cr + process_Huffman_data_unit(priv, cCr); +} + +/* + * Decode a 1x2 mcu + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void decode_MCU_1x2_3planes(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 64, 8); + + // Cb + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 1x2 mcu + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void decode_MCU_1x2_1plane(struct jdec_private *priv) +{ + // Y + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y + 64, 8); + + // Cb + process_Huffman_data_unit(priv, cCb); + + // Cr + process_Huffman_data_unit(priv, cCr); +} + +static void print_SOF(const unsigned char *stream) +{ +#if DEBUG + int width, height, nr_components, precision; + const char *nr_components_to_string[] = { + "????", + "Grayscale", + "????", + "YCbCr", + "CYMK" + }; + + precision = stream[2]; + height = be16_to_cpu(stream + 3); + width = be16_to_cpu(stream + 5); + nr_components = stream[7]; + + trace("> SOF marker\n"); + trace("Size:%dx%d nr_components:%d (%s) precision:%d\n", + width, height, + nr_components, nr_components_to_string[nr_components], + precision); +#endif +} + +/******************************************************************************* + * + * JPEG/JFIF Parsing functions + * + * Note: only a small subset of the jpeg file format is supported. No markers, + * nor progressive stream is supported. + * + ******************************************************************************/ + +static void build_quantization_table(float *qtable, const unsigned char *ref_table) +{ + /* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct. + * For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + int i, j; + static const double aanscalefactor[8] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + const unsigned char *zz = zigzag; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; + +} + +static int parse_DQT(struct jdec_private *priv, const unsigned char *stream) +{ + int qi; + float *table; + const unsigned char *dqt_block_end; + + trace("> DQT marker\n"); + dqt_block_end = stream + be16_to_cpu(stream); + stream += 2; /* Skip length */ + + while (stream < dqt_block_end) { + qi = *stream++; +#if SANITY_CHECK + if (qi >> 4) + error("16 bits quantization table is not supported\n"); + if (qi >= COMPONENTS) + error("No more than %d quantization tables supported (got %d)\n", + COMPONENTS, qi + 1); +#endif + table = priv->Q_tables[qi]; + build_quantization_table(table, stream); + stream += 64; + } + trace("< DQT marker\n"); + return 0; +} + +static int parse_SOF(struct jdec_private *priv, const unsigned char *stream) +{ + int i, width, height, nr_components, cid, sampling_factor; + int Q_table; + struct component *c; + + trace("> SOF marker\n"); + print_SOF(stream); + + height = be16_to_cpu(stream+3); + width = be16_to_cpu(stream+5); + nr_components = stream[7]; +#if SANITY_CHECK + if (stream[2] != 8) + error("Precision other than 8 is not supported\n"); + if (width > JPEG_MAX_WIDTH || height > JPEG_MAX_HEIGHT) + error("Width and Height (%dx%d) seems suspicious\n", width, height); + if (nr_components != 3) + error("We only support YUV images\n"); + if (height % 8) + error("Height need to be a multiple of 8 (current height is %d)\n", height); + if (width % 16) + error("Width need to be a multiple of 16 (current Width is %d)\n", width); +#endif + stream += 8; + for (i = 0; i < nr_components; i++) { + cid = *stream++; + sampling_factor = *stream++; + Q_table = *stream++; + c = &priv->component_infos[i]; +#if SANITY_CHECK + c->cid = cid; +#endif + c->Vfactor = sampling_factor & 0xf; + c->Hfactor = sampling_factor >> 4; + c->Q_table = priv->Q_tables[Q_table]; + trace("Component:%d factor:%dx%d Quantization table:%d\n", + cid, c->Hfactor, c->Hfactor, Q_table); + + } + priv->width = width; + priv->height = height; + + trace("< SOF marker\n"); + + return 0; +} + +static int parse_SOS(struct jdec_private *priv, const unsigned char *stream) +{ + unsigned int i, cid, table; + unsigned int nr_components = stream[2]; + + trace("> SOS marker\n"); + +#if SANITY_CHECK + if (nr_components != 3 && nr_components != 1) + error("We only support YCbCr image\n"); +#endif + + if (nr_components == 1) + priv->flags |= TINYJPEG_FLAGS_PLANAR_JPEG; +#if SANITY_CHECK + else if (priv->flags & TINYJPEG_FLAGS_PLANAR_JPEG) + error("SOS with more then 1 component while decoding planar JPEG\n"); +#endif + + stream += 3; + for (i = 0; i < nr_components; i++) { + cid = *stream++; + table = *stream++; + if (nr_components == 1) { +#if SANITY_CHECK + /* Find matching cid so we store the tables in the right component */ + for (i = 0; i < COMPONENTS; i++) + if (priv->component_infos[i].cid == cid) + break; + + if (i == COMPONENTS) + error("Unknown cid in SOS: %u\n", cid); + + priv->current_cid = cid; +#else + i = cid - 1; +#endif + trace("SOS cid: %u, using component_info: %u\n", cid, i); + } +#if SANITY_CHECK + if ((table & 0xf) >= HUFFMAN_TABLES) + error("We do not support more than %d AC Huffman table\n", + HUFFMAN_TABLES); + if ((table >> 4) >= HUFFMAN_TABLES) + error("We do not support more than %d DC Huffman table\n", + HUFFMAN_TABLES); + if (cid != priv->component_infos[i].cid) + error("SOS cid order (%u:%u) isn't compatible with the SOF marker (%u:%u)\n", + i, cid, i, priv->component_infos[i].cid); + trace("ComponentId:%u tableAC:%d tableDC:%d\n", cid, table & 0xf, table >> 4); +#endif + priv->component_infos[i].AC_table = &priv->HTAC[table & 0xf]; + priv->component_infos[i].DC_table = &priv->HTDC[table >> 4]; + } + priv->stream = stream + 3; + + /* ITU-T T.81 (9/92) chapter E.1.3 clearly states that RSTm is to be set to 0 at the beginning of each scan */ + priv->last_rst_marker_seen = 0; + + trace("< SOS marker\n"); + + return 0; +} + +static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) +{ + unsigned int count, i; + unsigned char huff_bits[17]; + int length, index; + + length = be16_to_cpu(stream) - 2; + stream += 2; /* Skip length */ + + trace("> DHT marker (length=%d)\n", length); + + while (length > 0) { + index = *stream++; + + /* We need to calculate the number of bytes 'vals' will takes */ + huff_bits[0] = 0; + count = 0; + for (i = 1; i < 17; i++) { + huff_bits[i] = *stream++; + count += huff_bits[i]; + } +#if SANITY_CHECK + if (count > 1024) + error("No more than 1024 bytes is allowed to describe a huffman table\n"); + if ((index & 0xf) >= HUFFMAN_TABLES) + error("No mode than %d Huffman tables is supported\n", HUFFMAN_TABLES); + trace("Huffman table %s n%d\n", (index & 0xf0) ? "AC" : "DC", index & 0xf); + trace("Length of the table: %d\n", count); +#endif + + if (index & 0xf0) { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTAC[index & 0xf])) + return -1; + } else { + if (build_huffman_table(priv, huff_bits, stream, &priv->HTDC[index & 0xf])) + return -1; + } + + length -= 1; + length -= 16; + length -= count; + stream += count; + } + trace("< DHT marker\n"); + return 0; +} + +static int parse_DRI(struct jdec_private *priv, const unsigned char *stream) +{ + unsigned int length; + + trace("> DRI marker\n"); + + length = be16_to_cpu(stream); + +#if SANITY_CHECK + if (length != 4) + error("Length of DRI marker need to be 4\n"); +#endif + + priv->restart_interval = be16_to_cpu(stream + 2); + +#if DEBUG + trace("Restart interval = %d\n", priv->restart_interval); +#endif + + trace("< DRI marker\n"); + + return 0; +} + + + +static void resync(struct jdec_private *priv) +{ + int i; + + /* Init DC coefficients */ + for (i = 0; i < COMPONENTS; i++) + priv->component_infos[i].previous_DC = 0; + + priv->reservoir = 0; + priv->nbits_in_reservoir = 0; + if (priv->restart_interval > 0) + priv->restarts_to_go = priv->restart_interval; + else + priv->restarts_to_go = -1; +} + +static int find_next_rst_marker(struct jdec_private *priv) +{ + int rst_marker_found = 0; + int marker; + const unsigned char *stream = priv->stream; + + /* Parse marker */ + while (!rst_marker_found) { + while (*stream++ != 0xff) { + if (stream >= priv->stream_end) + error("EOF while search for a RST marker.\n"); + } + /* Skip any padding ff byte (this is normal) */ + while (*stream == 0xff) { + stream++; + if (stream >= priv->stream_end) + error("EOF while search for a RST marker.\n"); + } + + marker = *stream++; + if ((RST + priv->last_rst_marker_seen) == marker) + rst_marker_found = 1; + else if (marker >= RST && marker <= RST7) + error("Wrong Reset marker found, abording\n"); + else if (marker == EOI) + return 0; + } + + priv->stream = stream; + priv->last_rst_marker_seen++; + priv->last_rst_marker_seen &= 7; + + return 0; +} + +static int find_next_sos_marker(struct jdec_private *priv) +{ + const unsigned char *stream = priv->stream; + + /* Parse marker */ + while (1) { + while (*stream++ != 0xff) { + if (stream >= priv->stream_end) + error("EOF while search for a SOS marker.\n"); + } + /* Skip any padding ff byte (this is normal) */ + while (*stream == 0xff) { + stream++; + if (stream >= priv->stream_end) + error("EOF while search for a SOS marker.\n"); + } + + if (*stream++ == SOS) + break; /* Found it ! */ + } + + priv->stream = stream; + + return 0; +} + +static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) +{ + int chuck_len; + int marker; + int sof_marker_found = 0; + int dqt_marker_found = 0; + int sos_marker_found = 0; + int dht_marker_found = 0; + const unsigned char *next_chunck; + + /* Parse marker */ + while (!sos_marker_found) { + if (*stream++ != 0xff) + goto bogus_jpeg_format; + /* Skip any padding ff byte (this is normal) */ + while (*stream == 0xff) + stream++; + + marker = *stream++; + chuck_len = be16_to_cpu(stream); + next_chunck = stream + chuck_len; + switch (marker) { + case SOF: + if (parse_SOF(priv, stream) < 0) + return -1; + sof_marker_found = 1; + break; + case DQT: + if (parse_DQT(priv, stream) < 0) + return -1; + dqt_marker_found = 1; + break; + case SOS: + if (parse_SOS(priv, stream) < 0) + return -1; + sos_marker_found = 1; + break; + case DHT: + if (parse_DHT(priv, stream) < 0) + return -1; + dht_marker_found = 1; + break; + case DRI: + if (parse_DRI(priv, stream) < 0) + return -1; + break; + default: + trace("> Unknown marker %2.2x\n", marker); + break; + } + + stream = next_chunck; + } + + if (!sof_marker_found || + (!dqt_marker_found && !(priv->flags & TINYJPEG_FLAGS_PIXART_JPEG))) + goto bogus_jpeg_format; + + if (!dht_marker_found) { + trace("No Huffman table loaded, using the default one\n"); + if (build_default_huffman_tables(priv)) + return -1; + } + +#ifdef SANITY_CHECK + if ((priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor) + || (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor)) + error("Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n"); + if ((priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor) + || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor)) + error("Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n"); + if ((priv->component_infos[cCb].Hfactor != 1) + || (priv->component_infos[cCr].Hfactor != 1) + || (priv->component_infos[cCb].Vfactor != 1) + || (priv->component_infos[cCr].Vfactor != 1)) + error("Sampling other than 1x1 for Cr and Cb is not supported\n"); + if ((priv->flags & TINYJPEG_FLAGS_PLANAR_JPEG) && + ((priv->component_infos[cY].Hfactor != 2) + || (priv->component_infos[cY].Hfactor != 2))) + error("Sampling other than 2x2 for Y is not supported with planar JPEG\n"); +#endif + + return 0; +bogus_jpeg_format: + error("Bogus jpeg format\n"); + return -1; +} + +/******************************************************************************* + * + * Functions exported of the library. + * + * Note: Some applications can access directly to internal pointer of the + * structure. It's is not recommended, but if you have many images to + * uncompress with the same parameters, some functions can be called to speedup + * the decoding. + * + ******************************************************************************/ + +/** + * Allocate a new tinyjpeg decoder object. + * + * Before calling any other functions, an object need to be called. + */ +struct jdec_private *tinyjpeg_init(void) +{ + struct jdec_private *priv; + + priv = (struct jdec_private *)calloc(1, sizeof(struct jdec_private)); + if (priv == NULL) + return NULL; + return priv; +} + +/** + * Free a tinyjpeg object. + * + * No others function can be called after this one. + */ +void tinyjpeg_free(struct jdec_private *priv) +{ + int i; + + for (i = 0; i < COMPONENTS; i++) { + free(priv->components[i]); + free(priv->tmp_buf[i]); + priv->components[i] = NULL; + priv->tmp_buf[i] = NULL; + } + priv->tmp_buf_y_size = 0; + free(priv->stream_filtered); + free(priv); +} + +/** + * Initialize the tinyjpeg object and prepare the decoding of the stream. + * + * Check if the jpeg can be decoded with this jpeg decoder. + * Fill some table used for preprocessing. + */ +int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size) +{ + /* Identify the file */ + if ((buf[0] != 0xFF) || (buf[1] != SOI)) + error("Not a JPG file ?\n"); + + priv->stream_end = buf + size; + + return parse_JFIF(priv, buf + 2); +} + +static const decode_MCU_fct decode_mcu_3comp_table[4] = { + decode_MCU_1x1_3planes, + decode_MCU_1x2_3planes, + decode_MCU_2x1_3planes, + decode_MCU_2x2_3planes, +}; + +static const decode_MCU_fct pixart_decode_mcu_3comp_table[4] = { + NULL, + NULL, + pixart_decode_MCU_2x1_3planes, + NULL, +}; + +static const decode_MCU_fct decode_mcu_1comp_table[4] = { + decode_MCU_1x1_1plane, + decode_MCU_1x2_1plane, + decode_MCU_2x1_1plane, + decode_MCU_2x2_1plane, +}; + +static const convert_colorspace_fct convert_colorspace_yuv420p[4] = { + YCrCB_to_YUV420P_1x1, + YCrCB_to_YUV420P_1x2, + YCrCB_to_YUV420P_2x1, + YCrCB_to_YUV420P_2x2, +}; + +static const convert_colorspace_fct convert_colorspace_rgb24[4] = { + YCrCB_to_RGB24_1x1, + YCrCB_to_RGB24_1x2, + YCrCB_to_RGB24_2x1, + YCrCB_to_RGB24_2x2, +}; + +static const convert_colorspace_fct convert_colorspace_bgr24[4] = { + YCrCB_to_BGR24_1x1, + YCrCB_to_BGR24_1x2, + YCrCB_to_BGR24_2x1, + YCrCB_to_BGR24_2x2, +}; + +static const convert_colorspace_fct convert_colorspace_grey[4] = { + YCrCB_to_Grey_1x1, + YCrCB_to_Grey_1x2, + YCrCB_to_Grey_2x1, + YCrCB_to_Grey_2x2, +}; + +int tinyjpeg_decode_planar(struct jdec_private *priv, int pixfmt); + +/* This function parses and removes the special Pixart JPEG chunk headers */ +static int pixart_filter(struct jdec_private *priv, unsigned char *dest, + const unsigned char *src, int n) +{ + int chunksize, copied = 0; + + /* The first data bytes encodes the image size: + 0x60: 160x120 + 0x61: 320x240 + 0x62: 640x480 + 160x120 images are not chunked due to their small size! + */ + if (src[0] == 0x60) { + memcpy(dest, src + 1, n - 1); + return n - 1; + } + + src++; + n--; + + /* The first chunk is always 1024 bytes, 5 bytes are dropped in the +kernel: 0xff 0xff 0x00 0xff 0x96, and we skip one unknown byte */ + chunksize = 1024 - 6; + + while (1) { + if (n < chunksize) + break; /* Short frame */ + + memcpy(dest, src, chunksize); + dest += chunksize; + src += chunksize; + copied += chunksize; + n -= chunksize; + + if (n < 4) + break; /* Short frame */ + + if (src[0] != 0xff || src[1] != 0xff || src[2] != 0xff) + error("Missing Pixart ff ff ff xx header, " + "got: %02x %02x %02x %02x, copied sofar: %d\n", + src[0], src[1], src[2], src[3], copied); + if (src[3] > 6) + error("Unexpected Pixart chunk size: %d\n", src[3]); + + chunksize = src[3]; + src += 4; + n -= 4; + + if (chunksize == 0) { + /* 0 indicates we are done, copy whatever remains */ + memcpy(dest, src, n); + return copied + n; + } + + chunksize = 2048 >> chunksize; + } + error("Short Pixart JPEG frame\n"); +} + +/** + * Decode and convert the jpeg image into @pixfmt@ image + * + * Note: components will be automaticaly allocated if no memory is attached. + */ +int tinyjpeg_decode(struct jdec_private *priv, int pixfmt) +{ + unsigned int x, y, xstride_by_mcu, ystride_by_mcu; + unsigned int bytes_per_blocklines[3], bytes_per_mcu[3]; + decode_MCU_fct decode_MCU; + const decode_MCU_fct *decode_mcu_table; + const convert_colorspace_fct *colorspace_array_conv; + convert_colorspace_fct convert_to_pixfmt; + + if (setjmp(priv->jump_state)) + return -1; + + if (priv->flags & TINYJPEG_FLAGS_PLANAR_JPEG) + return tinyjpeg_decode_planar(priv, pixfmt); + + /* To keep gcc happy initialize some array */ + bytes_per_mcu[1] = 0; + bytes_per_mcu[2] = 0; + bytes_per_blocklines[1] = 0; + bytes_per_blocklines[2] = 0; + + decode_mcu_table = decode_mcu_3comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + int length; + + priv->stream_filtered = + v4lconvert_alloc_buffer(priv->stream_end - priv->stream, + &priv->stream_filtered, + &priv->stream_filtered_bufsize); + if (!priv->stream_filtered) + error("Out of memory!\n"); + + length = pixart_filter(priv, priv->stream_filtered, + priv->stream, priv->stream_end - priv->stream); + if (length < 0) + return length; + priv->stream = priv->stream_filtered; + priv->stream_end = priv->stream + length; + priv->first_marker = 0; + + decode_mcu_table = pixart_decode_mcu_3comp_table; + } + + switch (pixfmt) { + case TINYJPEG_FMT_YUV420P: + colorspace_array_conv = convert_colorspace_yuv420p; + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); + if (priv->components[1] == NULL) + priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4); + if (priv->components[2] == NULL) + priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4); + bytes_per_blocklines[0] = priv->width; + bytes_per_blocklines[1] = priv->width/4; + bytes_per_blocklines[2] = priv->width/4; + bytes_per_mcu[0] = 8; + bytes_per_mcu[1] = 4; + bytes_per_mcu[2] = 4; + break; + + case TINYJPEG_FMT_RGB24: + colorspace_array_conv = convert_colorspace_rgb24; + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3); + bytes_per_blocklines[0] = priv->width * 3; + bytes_per_mcu[0] = 3*8; + break; + + case TINYJPEG_FMT_BGR24: + colorspace_array_conv = convert_colorspace_bgr24; + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3); + bytes_per_blocklines[0] = priv->width * 3; + bytes_per_mcu[0] = 3*8; + break; + + case TINYJPEG_FMT_GREY: + decode_mcu_table = decode_mcu_1comp_table; + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) + error("Greyscale output not support for PIXART JPEG's\n"); + colorspace_array_conv = convert_colorspace_grey; + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); + bytes_per_blocklines[0] = priv->width; + bytes_per_mcu[0] = 8; + break; + + default: + error("Bad pixel format\n"); + } + + xstride_by_mcu = ystride_by_mcu = 8; + if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) { + decode_MCU = decode_mcu_table[0]; + convert_to_pixfmt = colorspace_array_conv[0]; + trace("Use decode 1x1 sampling\n"); + } else if (priv->component_infos[cY].Hfactor == 1) { + decode_MCU = decode_mcu_table[1]; + convert_to_pixfmt = colorspace_array_conv[1]; + ystride_by_mcu = 16; + trace("Use decode 1x2 sampling (not supported)\n"); + } else if (priv->component_infos[cY].Vfactor == 2) { + decode_MCU = decode_mcu_table[3]; + convert_to_pixfmt = colorspace_array_conv[3]; + xstride_by_mcu = 16; + ystride_by_mcu = 16; + trace("Use decode 2x2 sampling\n"); + } else { + decode_MCU = decode_mcu_table[2]; + convert_to_pixfmt = colorspace_array_conv[2]; + xstride_by_mcu = 16; + trace("Use decode 2x1 sampling\n"); + } + + if (decode_MCU == NULL) + error("no decode MCU function for this JPEG format (PIXART?)\n"); + + resync(priv); + + /* Don't forget to that block can be either 8 or 16 lines */ + bytes_per_blocklines[0] *= ystride_by_mcu; + bytes_per_blocklines[1] *= ystride_by_mcu; + bytes_per_blocklines[2] *= ystride_by_mcu; + + bytes_per_mcu[0] *= xstride_by_mcu / 8; + bytes_per_mcu[1] *= xstride_by_mcu / 8; + bytes_per_mcu[2] *= xstride_by_mcu / 8; + + /* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */ + for (y = 0; y < priv->height / ystride_by_mcu; y++) { + //trace("Decoding row %d\n", y); + priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]); + priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]); + priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]); + for (x = 0; x < priv->width; x += xstride_by_mcu) { + decode_MCU(priv); + convert_to_pixfmt(priv); + priv->plane[0] += bytes_per_mcu[0]; + priv->plane[1] += bytes_per_mcu[1]; + priv->plane[2] += bytes_per_mcu[2]; + if (priv->restarts_to_go > 0) { + priv->restarts_to_go--; + if (priv->restarts_to_go == 0) { + priv->stream -= (priv->nbits_in_reservoir / 8); + resync(priv); + if (find_next_rst_marker(priv) < 0) + return -1; + } + } + } + } + + if (priv->flags & TINYJPEG_FLAGS_PIXART_JPEG) { + /* Additional sanity check for funky Pixart format */ + if ((priv->stream_end - priv->stream) > 5) + error("Pixart JPEG error, stream does not end with EOF marker\n"); + } + + return 0; +} + +int tinyjpeg_decode_planar(struct jdec_private *priv, int pixfmt) +{ + unsigned int i, x, y; + uint8_t *y_buf, *u_buf, *v_buf, *p, *p2; + + switch (pixfmt) { + case TINYJPEG_FMT_GREY: + error("Greyscale output not supported with planar JPEG input\n"); + break; + + case TINYJPEG_FMT_RGB24: + case TINYJPEG_FMT_BGR24: + if (priv->tmp_buf_y_size < (priv->width * priv->height)) { + for (i = 0; i < COMPONENTS; i++) { + free(priv->tmp_buf[i]); + priv->tmp_buf[i] = malloc(priv->width * priv->height / (i ? 4 : 1)); + if (!priv->tmp_buf[i]) + error("Could not allocate memory for temporary buffers\n"); + } + priv->tmp_buf_y_size = priv->width * priv->height; + } + y_buf = priv->tmp_buf[cY]; + u_buf = priv->tmp_buf[cCb]; + v_buf = priv->tmp_buf[cCr]; + break; + + case TINYJPEG_FMT_YUV420P: + y_buf = priv->components[cY]; + u_buf = priv->components[cCb]; + v_buf = priv->components[cCr]; + break; + + default: + error("Bad pixel format\n"); + } + +#if SANITY_CHECK + if (priv->current_cid != priv->component_infos[cY].cid) + error("Planar jpeg first SOS cid does not match Y cid (%u:%u)\n", + priv->current_cid, priv->component_infos[cY].cid); +#endif + + resync(priv); + + for (y = 0; y < priv->height / 8; y++) { + for (x = 0; x < priv->width / 8; x++) { + process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], y_buf, priv->width); + y_buf += 8; + } + y_buf += 7 * priv->width; + } + + priv->stream -= (priv->nbits_in_reservoir/8); + resync(priv); + if (find_next_sos_marker(priv) < 0) + return -1; + if (parse_SOS(priv, priv->stream) < 0) + return -1; + +#if SANITY_CHECK + if (priv->current_cid != priv->component_infos[cCb].cid) + error("Planar jpeg second SOS cid does not match Cn cid (%u:%u)\n", + priv->current_cid, priv->component_infos[cCb].cid); +#endif + + for (y = 0; y < priv->height / 16; y++) { + for (x = 0; x < priv->width / 16; x++) { + process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], u_buf, priv->width / 2); + u_buf += 8; + } + u_buf += 7 * (priv->width / 2); + } + + priv->stream -= (priv->nbits_in_reservoir / 8); + resync(priv); + if (find_next_sos_marker(priv) < 0) + return -1; + if (parse_SOS(priv, priv->stream) < 0) + return -1; + +#if SANITY_CHECK + if (priv->current_cid != priv->component_infos[cCr].cid) + error("Planar jpeg third SOS cid does not match Cr cid (%u:%u)\n", + priv->current_cid, priv->component_infos[cCr].cid); +#endif + + for (y = 0; y < priv->height / 16; y++) { + for (x = 0; x < priv->width / 16; x++) { + process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], v_buf, priv->width / 2); + v_buf += 8; + } + v_buf += 7 * (priv->width / 2); + } + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS - 1)) +#define FIX(x) ((int)((x) * (1UL << SCALEBITS) + 0.5)) + + switch (pixfmt) { + case TINYJPEG_FMT_RGB24: + y_buf = priv->tmp_buf[cY]; + u_buf = priv->tmp_buf[cCb]; + v_buf = priv->tmp_buf[cCr]; + p = priv->components[0]; + p2 = priv->components[0] + priv->width * 3; + + for (y = 0; y < priv->height / 2; y++) { + for (x = 0; x < priv->width / 2; x++) { + int l, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *u_buf++ - 128; + cr = *v_buf++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + l = (*y_buf) << SCALEBITS; + r = (l + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (l + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (l + add_b) >> SCALEBITS; + *p++ = clamp(b); + + l = (y_buf[priv->width]) << SCALEBITS; + r = (l + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (l + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (l + add_b) >> SCALEBITS; + *p2++ = clamp(b); + + y_buf++; + + l = (*y_buf) << SCALEBITS; + r = (l + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (l + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (l + add_b) >> SCALEBITS; + *p++ = clamp(b); + + l = (y_buf[priv->width]) << SCALEBITS; + r = (l + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (l + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (l + add_b) >> SCALEBITS; + *p2++ = clamp(b); + + y_buf++; + } + y_buf += priv->width; + p += priv->width * 3; + p2 += priv->width * 3; + } + break; + + case TINYJPEG_FMT_BGR24: + y_buf = priv->tmp_buf[cY]; + u_buf = priv->tmp_buf[cCb]; + v_buf = priv->tmp_buf[cCr]; + p = priv->components[0]; + p2 = priv->components[0] + priv->width * 3; + + for (y = 0; y < priv->height / 2; y++) { + for (x = 0; x < priv->width / 2; x++) { + int l, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *u_buf++ - 128; + cr = *v_buf++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = -FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + l = (*y_buf) << SCALEBITS; + b = (l + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (l + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (l + add_r) >> SCALEBITS; + *p++ = clamp(r); + + l = (y_buf[priv->width]) << SCALEBITS; + b = (l + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (l + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (l + add_r) >> SCALEBITS; + *p2++ = clamp(r); + + y_buf++; + + l = (*y_buf) << SCALEBITS; + b = (l + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (l + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (l + add_r) >> SCALEBITS; + *p++ = clamp(r); + + l = (y_buf[priv->width]) << SCALEBITS; + b = (l + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (l + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (l + add_r) >> SCALEBITS; + *p2++ = clamp(r); + + y_buf++; + } + y_buf += priv->width; + p += priv->width * 3; + p2 += priv->width * 3; + } + break; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + + return 0; +} + +const char *tinyjpeg_get_errorstring(struct jdec_private *priv) +{ + return priv->error_string; +} + +void tinyjpeg_get_size(struct jdec_private *priv, unsigned int *width, unsigned int *height) +{ + *width = priv->width; + *height = priv->height; +} + +int tinyjpeg_get_components(struct jdec_private *priv, unsigned char **components) +{ + int i; + + for (i = 0; i < COMPONENTS && priv->components[i]; i++) + components[i] = priv->components[i]; + return 0; +} + +int tinyjpeg_set_components(struct jdec_private *priv, unsigned char **components, unsigned int ncomponents) +{ + unsigned int i; + + if (ncomponents > COMPONENTS) + ncomponents = COMPONENTS; + for (i = 0; i < ncomponents; i++) + priv->components[i] = components[i]; + return 0; +} + +int tinyjpeg_set_flags(struct jdec_private *priv, int flags) +{ + int oldflags = priv->flags; + + priv->flags = flags; + return oldflags; +} + diff --git a/libv4lconvert/tinyjpeg.h b/libv4lconvert/tinyjpeg.h new file mode 100644 index 0000000..ee61d23 --- /dev/null +++ b/libv4lconvert/tinyjpeg.h @@ -0,0 +1,76 @@ +/* + * Small jpeg decoder library (header file) + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef __JPEGDEC_H__ +#define __JPEGDEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct jdec_private; + +/* Flags that can be set by any applications */ +#define TINYJPEG_FLAGS_MJPEG_TABLE (1<<1) +#define TINYJPEG_FLAGS_PIXART_JPEG (1<<2) +#define TINYJPEG_FLAGS_PLANAR_JPEG (1<<3) + +/* Format accepted in outout */ +enum tinyjpeg_fmt { + TINYJPEG_FMT_GREY = 1, + TINYJPEG_FMT_BGR24, + TINYJPEG_FMT_RGB24, + TINYJPEG_FMT_YUV420P, +}; + +struct jdec_private *tinyjpeg_init(void); +void tinyjpeg_free(struct jdec_private *priv); + +int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size); +int tinyjpeg_decode(struct jdec_private *priv, int pixel_format); +const char *tinyjpeg_get_errorstring(struct jdec_private *priv); +void tinyjpeg_get_size(struct jdec_private *priv, unsigned int *width, unsigned int *height); +int tinyjpeg_get_components(struct jdec_private *priv, unsigned char **components); +int tinyjpeg_set_components(struct jdec_private *priv, unsigned char **components, + unsigned int ncomponents); +int tinyjpeg_set_flags(struct jdec_private *priv, int flags); + +#ifdef __cplusplus +} +#endif + +#endif + + + diff --git a/push_info.txt b/push_info.txt new file mode 100644 index 0000000..0363cca --- /dev/null +++ b/push_info.txt @@ -0,0 +1 @@ +jetson_35.6 diff --git a/v4l2libs_README.txt b/v4l2libs_README.txt new file mode 100644 index 0000000..52e8425 --- /dev/null +++ b/v4l2libs_README.txt @@ -0,0 +1,31 @@ + README + +v4l2_libs_src.tbz2 has following structure: +- include +- libv4l2 +- libv4lconvert +- v4l2libs_README.txt +- LICENSE.v4l2libs + +This tar contains sources for following two libraries: +- libnvv4l2 +- libnvv4lconvert + +1) Steps for building libnvv4lconvert: + + cd "libv4lconvert" + make + sudo make install + + Above steps will build and install libnvv4lconvert.so at + /usr/lib/aarch64-linux-gnu/tegra + +2) Steps for building libnvv4l2: + + Build and install libnvv4lconvert as mentioned above. + cd "libv4l2" + make + sudo make install + + Above steps will build and install libnvv4l2.so at + /usr/lib/aarch64-linux-gnu/tegra