| /* |
| * Copyright (C) 2007 Haakon Sporsheim <hakon.sporsheim@tandberg.com> |
| * 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> |
| * 2009 Knut Inge Hvidsten <knut.inge.hvidsten@tandberg.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * 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 |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "ksvideohelpers.h" |
| |
| #include <math.h> |
| #include <uuids.h> |
| #include "kshelpers.h" |
| |
| GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug); |
| #define GST_CAT_DEFAULT gst_ks_debug |
| |
| static const GUID MEDIASUBTYPE_FOURCC = |
| { 0x0 /* FourCC here */ , 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xAA, 0x00, |
| 0x38, 0x9B, 0x71} |
| }; |
| |
| typedef struct _KsVideoDeviceEntry KsVideoDeviceEntry; |
| |
| struct _KsVideoDeviceEntry |
| { |
| KsDeviceEntry *device; |
| gint priority; |
| }; |
| |
| static void |
| ks_video_device_entry_decide_priority (KsVideoDeviceEntry * videodevice) |
| { |
| HANDLE filter_handle; |
| |
| videodevice->priority = 0; |
| |
| filter_handle = CreateFile (videodevice->device->path, |
| GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, |
| FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); |
| if (ks_is_valid_handle (filter_handle)) { |
| GUID *propsets = NULL; |
| gulong propsets_len; |
| |
| if (ks_object_get_supported_property_sets (filter_handle, &propsets, |
| &propsets_len)) { |
| gulong i; |
| |
| for (i = 0; i < propsets_len; i++) { |
| if (memcmp (&propsets[i], &PROPSETID_VIDCAP_CAMERACONTROL, |
| sizeof (GUID)) == 0) { |
| videodevice->priority++; |
| break; |
| } |
| } |
| |
| g_free (propsets); |
| } |
| } |
| |
| CloseHandle (filter_handle); |
| } |
| |
| static gint |
| ks_video_device_entry_compare (gconstpointer a, gconstpointer b) |
| { |
| const KsVideoDeviceEntry *videodevice_a = a; |
| const KsVideoDeviceEntry *videodevice_b = b; |
| |
| if (videodevice_a->priority > videodevice_b->priority) |
| return -1; |
| else if (videodevice_a->priority == videodevice_b->priority) |
| return 0; |
| else |
| return 1; |
| } |
| |
| GList * |
| ks_video_device_list_sort_cameras_first (GList * devices) |
| { |
| GList *videodevices = NULL, *walk; |
| guint i; |
| |
| for (walk = devices; walk != NULL; walk = walk->next) { |
| KsDeviceEntry *device = walk->data; |
| KsVideoDeviceEntry *videodevice; |
| |
| videodevice = g_new (KsVideoDeviceEntry, 1); |
| videodevice->device = device; |
| ks_video_device_entry_decide_priority (videodevice); |
| |
| videodevices = g_list_append (videodevices, videodevice); |
| } |
| |
| videodevices = g_list_sort (videodevices, ks_video_device_entry_compare); |
| |
| g_list_free (devices); |
| devices = NULL; |
| |
| for (walk = videodevices, i = 0; walk != NULL; walk = walk->next, i++) { |
| KsVideoDeviceEntry *videodevice = walk->data; |
| |
| videodevice->device->index = i; |
| devices = g_list_append (devices, videodevice->device); |
| |
| g_free (videodevice); |
| } |
| |
| g_list_free (videodevices); |
| |
| return devices; |
| } |
| |
| static GstStructure * |
| ks_video_format_to_structure (GUID subtype_guid, GUID format_guid, |
| gboolean * p_is_rgb) |
| { |
| GstStructure *structure = NULL; |
| const gchar *media_type = NULL, *format = NULL; |
| /* RGB formats can be bottom-up (upside down) DIB */ |
| gboolean is_rgb = FALSE; |
| |
| if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_MJPG) || IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_TVMJ) || /* FIXME: NOT tested */ |
| IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_WAKE) || /* FIXME: NOT tested */ |
| IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_CFCC) || /* FIXME: NOT tested */ |
| IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_IJPG)) { /* FIXME: NOT tested */ |
| media_type = "image/jpeg"; |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB555)) { |
| media_type = "video/x-raw"; |
| format = "RGB15"; |
| is_rgb = TRUE; |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB565)) { |
| media_type = "video/x-raw"; |
| format = "RGB16"; |
| is_rgb = TRUE; |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB24)) { |
| media_type = "video/x-raw"; |
| format = "BGR"; |
| is_rgb = TRUE; |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_RGB32)) { |
| media_type = "video/x-raw"; |
| format = "BGRx"; |
| is_rgb = TRUE; |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB32)) { |
| media_type = "video/x-raw"; |
| format = "BGRA"; |
| is_rgb = TRUE; |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB1555)) { |
| GST_WARNING ("Unsupported video format ARGB15555"); |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_ARGB4444)) { |
| GST_WARNING ("Unsupported video format ARGB4444"); |
| } else if (memcmp (&subtype_guid.Data2, &MEDIASUBTYPE_FOURCC.Data2, |
| sizeof (subtype_guid) - sizeof (subtype_guid.Data1)) == 0) { |
| guint8 *p = (guint8 *) & subtype_guid.Data1; |
| gchar *format = g_strdup_printf ("%c%c%c%c", p[0], p[1], p[2], p[3]); |
| structure = gst_structure_new ("video/x-raw", "format", |
| G_TYPE_STRING, format, NULL); |
| g_free (format); |
| } else if (IsEqualGUID (&subtype_guid, &MEDIASUBTYPE_dvsd)) { |
| if (IsEqualGUID (&format_guid, &FORMAT_DvInfo)) { |
| structure = gst_structure_new ("video/x-dv", |
| "systemstream", G_TYPE_BOOLEAN, TRUE, NULL); |
| } else if (IsEqualGUID (&format_guid, &FORMAT_VideoInfo)) { |
| structure = gst_structure_new ("video/x-dv", |
| "systemstream", G_TYPE_BOOLEAN, FALSE, |
| "format", G_TYPE_STRING, "dvsd", NULL); |
| } |
| } |
| |
| if (media_type) { |
| structure = gst_structure_new_empty (media_type); |
| if (format) { |
| gst_structure_set (structure, "format", G_TYPE_STRING, format, NULL); |
| } |
| if (p_is_rgb) { |
| *p_is_rgb = is_rgb; |
| } |
| } |
| |
| if (!structure) { |
| GST_DEBUG ("Unknown DirectShow Video GUID " |
| "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
| (guint) subtype_guid.Data1, subtype_guid.Data2, subtype_guid.Data3, |
| subtype_guid.Data4[0], subtype_guid.Data4[1], subtype_guid.Data4[2], |
| subtype_guid.Data4[3], subtype_guid.Data4[4], subtype_guid.Data4[5], |
| subtype_guid.Data4[6], subtype_guid.Data4[7]); |
| } |
| |
| return structure; |
| } |
| |
| static void |
| guess_aspect (gint width, gint height, gint * par_width, gint * par_height) |
| { |
| /* |
| * As we dont have access to the actual pixel aspect, we will try to do a |
| * best-effort guess. The guess is based on most sensors being either 4/3 |
| * or 16/9, and most pixel aspects being close to 1/1. |
| */ |
| if ((width == 768) && (height == 448)) { /* special case for w448p */ |
| *par_width = 28; |
| *par_height = 27; |
| } else { |
| if (((float) width / (float) height) < 1.2778) { |
| *par_width = 12; |
| *par_height = 11; |
| } else { |
| *par_width = 1; |
| *par_height = 1; |
| } |
| } |
| } |
| |
| /* NOTE: would probably be better to use a continued fractions approach here */ |
| static void |
| compress_fraction (gint64 in_num, gint64 in_den, gint64 * out_num, |
| gint64 * out_den) |
| { |
| gdouble on, od, orig; |
| guint denominators[] = { 1, 2, 3, 5, 7 }, i; |
| const gdouble max_loss = 0.1; |
| |
| on = in_num; |
| od = in_den; |
| orig = on / od; |
| |
| for (i = 0; i < G_N_ELEMENTS (denominators); i++) { |
| gint64 cur_n, cur_d; |
| gdouble cur, loss; |
| |
| cur_n = floor ((on / (od / (gdouble) denominators[i])) + 0.5); |
| cur_d = denominators[i]; |
| cur = (gdouble) cur_n / (gdouble) cur_d; |
| loss = fabs (cur - orig); |
| |
| if (loss <= max_loss) { |
| *out_num = cur_n; |
| *out_den = cur_d; |
| |
| return; |
| } |
| } |
| |
| *out_num = in_num; |
| *out_den = in_den; |
| } |
| |
| static gboolean |
| ks_video_append_video_stream_cfg_fields (GstStructure * structure, |
| const KS_VIDEO_STREAM_CONFIG_CAPS * vscc) |
| { |
| GValue val = { 0, }; |
| gint64 min_n, min_d; |
| gint64 max_n, max_d; |
| |
| g_return_val_if_fail (structure, FALSE); |
| g_return_val_if_fail (vscc, FALSE); |
| |
| /* width */ |
| if (vscc->MinOutputSize.cx == vscc->MaxOutputSize.cx) { |
| gst_structure_set (structure, |
| "width", G_TYPE_INT, vscc->MaxOutputSize.cx, NULL); |
| } else { |
| gst_structure_set (structure, |
| "width", GST_TYPE_INT_RANGE, |
| vscc->MinOutputSize.cx, vscc->MaxOutputSize.cx, NULL); |
| } |
| |
| /* height */ |
| if (vscc->MinOutputSize.cy == vscc->MaxOutputSize.cy) { |
| gst_structure_set (structure, |
| "height", G_TYPE_INT, vscc->MaxOutputSize.cy, NULL); |
| } else { |
| gst_structure_set (structure, |
| "height", GST_TYPE_INT_RANGE, |
| vscc->MinOutputSize.cy, vscc->MaxOutputSize.cy, NULL); |
| } |
| |
| /* framerate */ |
| compress_fraction (NANOSECONDS, vscc->MinFrameInterval, &min_n, &min_d); |
| compress_fraction (NANOSECONDS, vscc->MaxFrameInterval, &max_n, &max_d); |
| |
| if (min_n == max_n && min_d == max_d) { |
| g_value_init (&val, GST_TYPE_FRACTION); |
| gst_value_set_fraction (&val, max_n, max_d); |
| } else { |
| g_value_init (&val, GST_TYPE_FRACTION_RANGE); |
| gst_value_set_fraction_range_full (&val, max_n, max_d, min_n, min_d); |
| } |
| |
| gst_structure_set_value (structure, "framerate", &val); |
| g_value_unset (&val); |
| |
| { |
| gint par_width, par_height; |
| |
| guess_aspect (vscc->MaxOutputSize.cx, vscc->MaxOutputSize.cy, |
| &par_width, &par_height); |
| |
| gst_structure_set (structure, |
| "pixel-aspect-ratio", GST_TYPE_FRACTION, par_width, par_height, NULL); |
| } |
| |
| return TRUE; |
| } |
| |
| KsVideoMediaType * |
| ks_video_media_type_dup (KsVideoMediaType * media_type) |
| { |
| KsVideoMediaType *result = g_new (KsVideoMediaType, 1); |
| |
| memcpy (result, media_type, sizeof (KsVideoMediaType)); |
| |
| result->range = g_malloc (media_type->range->FormatSize); |
| memcpy ((gpointer) result->range, media_type->range, |
| media_type->range->FormatSize); |
| |
| result->format = g_malloc (media_type->format_size); |
| memcpy (result->format, media_type->format, media_type->format_size); |
| |
| result->translated_caps = gst_caps_ref (media_type->translated_caps); |
| |
| return result; |
| } |
| |
| void |
| ks_video_media_type_free (KsVideoMediaType * media_type) |
| { |
| if (media_type == NULL) |
| return; |
| |
| g_free ((gpointer) media_type->range); |
| |
| g_free (media_type->format); |
| |
| if (media_type->translated_caps != NULL) |
| gst_caps_unref (media_type->translated_caps); |
| |
| g_free (media_type); |
| } |
| |
| static GList * |
| ks_video_media_type_list_remove_duplicates (GList * media_types) |
| { |
| GList *master, *duplicates; |
| |
| do { |
| GList *entry; |
| |
| master = duplicates = NULL; |
| |
| /* Find the first set of duplicates and their master */ |
| for (entry = media_types; entry != NULL && duplicates == NULL; |
| entry = entry->next) { |
| KsVideoMediaType *mt = entry->data; |
| GList *other_entry; |
| |
| for (other_entry = media_types; other_entry != NULL; |
| other_entry = other_entry->next) { |
| KsVideoMediaType *other_mt = other_entry->data; |
| |
| if (other_mt == mt) |
| continue; |
| |
| if (gst_caps_is_equal (mt->translated_caps, other_mt->translated_caps)) |
| duplicates = g_list_prepend (duplicates, other_mt); |
| } |
| |
| if (duplicates != NULL) |
| master = entry; |
| } |
| |
| if (duplicates != NULL) { |
| KsVideoMediaType *selected_mt = master->data; |
| |
| /* |
| * Pick a FORMAT_VideoInfo2 if present, if not we just stay with the |
| * first entry |
| */ |
| for (entry = duplicates; entry != NULL; entry = entry->next) { |
| KsVideoMediaType *mt = entry->data; |
| |
| if (IsEqualGUID (&mt->range->Specifier, &FORMAT_VideoInfo2)) { |
| ks_video_media_type_free (selected_mt); |
| selected_mt = mt; |
| } else { |
| ks_video_media_type_free (mt); |
| } |
| |
| /* Remove the dupe from the main list */ |
| media_types = g_list_remove (media_types, mt); |
| } |
| |
| /* Update master node with the selected MediaType */ |
| master->data = selected_mt; |
| |
| g_list_free (duplicates); |
| } |
| } |
| while (master != NULL); |
| |
| return media_types; |
| } |
| |
| GList * |
| ks_video_probe_filter_for_caps (HANDLE filter_handle) |
| { |
| GList *ret = NULL; |
| gulong pin_count; |
| guint pin_id; |
| |
| if (!ks_filter_get_pin_property (filter_handle, 0, KSPROPSETID_Pin, |
| KSPROPERTY_PIN_CTYPES, &pin_count, sizeof (pin_count), NULL)) |
| goto beach; |
| |
| GST_DEBUG ("pin_count = %lu", pin_count); |
| |
| for (pin_id = 0; pin_id < pin_count; pin_id++) { |
| KSPIN_COMMUNICATION pin_comm; |
| KSPIN_DATAFLOW pin_flow; |
| GUID pin_cat; |
| |
| if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin, |
| KSPROPERTY_PIN_COMMUNICATION, &pin_comm, sizeof (pin_comm), NULL)) |
| continue; |
| |
| if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin, |
| KSPROPERTY_PIN_DATAFLOW, &pin_flow, sizeof (pin_flow), NULL)) |
| continue; |
| |
| if (!ks_filter_get_pin_property (filter_handle, pin_id, KSPROPSETID_Pin, |
| KSPROPERTY_PIN_CATEGORY, &pin_cat, sizeof (pin_cat), NULL)) |
| continue; |
| |
| GST_DEBUG ("pin[%u]: pin_comm=%d, pin_flow=%d", pin_id, pin_comm, pin_flow); |
| |
| if (pin_flow == KSPIN_DATAFLOW_OUT && |
| memcmp (&pin_cat, &PINNAME_CAPTURE, sizeof (GUID)) == 0) { |
| KSMULTIPLE_ITEM *items; |
| |
| if (ks_filter_get_pin_property_multi (filter_handle, pin_id, |
| KSPROPSETID_Pin, KSPROPERTY_PIN_DATARANGES, &items, NULL)) { |
| KSDATARANGE *range = (KSDATARANGE *) (items + 1); |
| guint i; |
| |
| for (i = 0; i < items->Count; i++) { |
| if (IsEqualGUID (&range->MajorFormat, &MEDIATYPE_Video) |
| || IsEqualGUID (&range->MajorFormat, &MEDIATYPE_Interleaved)) { |
| KsVideoMediaType *entry; |
| gpointer src_vscc, src_format; |
| GstStructure *media_structure; |
| |
| entry = g_new0 (KsVideoMediaType, 1); |
| entry->pin_id = pin_id; |
| |
| entry->range = g_malloc (range->FormatSize); |
| memcpy ((gpointer) entry->range, range, range->FormatSize); |
| |
| if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) { |
| KS_DATARANGE_VIDEO *vr = (KS_DATARANGE_VIDEO *) entry->range; |
| |
| src_vscc = &vr->ConfigCaps; |
| src_format = &vr->VideoInfoHeader; |
| |
| entry->format_size = sizeof (vr->VideoInfoHeader); |
| entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) { |
| KS_DATARANGE_VIDEO2 *vr = (KS_DATARANGE_VIDEO2 *) entry->range; |
| |
| src_vscc = &vr->ConfigCaps; |
| src_format = &vr->VideoInfoHeader; |
| |
| entry->format_size = sizeof (vr->VideoInfoHeader); |
| entry->sample_size = vr->VideoInfoHeader.bmiHeader.biSizeImage; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) { |
| /* Untested and probably wrong... */ |
| KS_DATARANGE_MPEG1_VIDEO *vr = |
| (KS_DATARANGE_MPEG1_VIDEO *) entry->range; |
| |
| src_vscc = &vr->ConfigCaps; |
| src_format = &vr->VideoInfoHeader; |
| |
| entry->format_size = sizeof (vr->VideoInfoHeader); |
| entry->sample_size = |
| vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) { |
| KS_DATARANGE_MPEG2_VIDEO *vr = |
| (KS_DATARANGE_MPEG2_VIDEO *) entry->range; |
| src_vscc = &vr->ConfigCaps; |
| src_format = &vr->VideoInfoHeader; |
| |
| entry->format_size = sizeof (vr->VideoInfoHeader); |
| entry->sample_size = |
| vr->VideoInfoHeader.hdr.bmiHeader.biSizeImage; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_DvInfo)) { |
| KS_DATARANGE_DVVIDEO *vr = (KS_DATARANGE_DVVIDEO *) entry->range; |
| |
| src_vscc = NULL; |
| src_format = &vr->DVVideoInfo; |
| |
| entry->format_size = sizeof (vr->DVVideoInfo); |
| entry->sample_size = vr->DataRange.SampleSize; |
| } else { |
| gchar *guid_str; |
| |
| guid_str = ks_guid_to_string (&range->Specifier); |
| GST_DEBUG ("pin[%u]: ignoring unknown specifier GUID %s", |
| pin_id, guid_str); |
| g_free (guid_str); |
| |
| ks_video_media_type_free (entry); |
| entry = NULL; |
| } |
| |
| if (entry != NULL) { |
| g_assert (entry->sample_size != 0); |
| |
| if (src_vscc != NULL) { |
| memcpy ((gpointer) & entry->vscc, src_vscc, |
| sizeof (entry->vscc)); |
| } |
| |
| entry->format = g_malloc (entry->format_size); |
| memcpy (entry->format, src_format, entry->format_size); |
| |
| media_structure = |
| ks_video_format_to_structure (range->SubFormat, |
| range->Specifier, &entry->is_rgb); |
| |
| if (media_structure == NULL) { |
| g_warning ("ks_video_format_to_structure returned NULL"); |
| ks_video_media_type_free (entry); |
| entry = NULL; |
| } else if (src_vscc == NULL) { |
| entry->translated_caps = gst_caps_new_empty (); |
| gst_caps_append_structure (entry->translated_caps, |
| media_structure); |
| } else if (ks_video_append_video_stream_cfg_fields |
| (media_structure, &entry->vscc)) { |
| entry->translated_caps = gst_caps_new_empty (); |
| gst_caps_append_structure (entry->translated_caps, |
| media_structure); |
| } else { |
| gst_structure_free (media_structure); |
| ks_video_media_type_free (entry); |
| entry = NULL; |
| } |
| |
| if (entry != NULL) |
| ret = g_list_prepend (ret, entry); |
| } |
| } |
| |
| /* REVISIT: Each KSDATARANGE should start on a 64-bit boundary */ |
| range = (KSDATARANGE *) (((guchar *) range) + range->FormatSize); |
| } |
| |
| g_free (items); |
| } |
| } |
| } |
| |
| if (ret != NULL) { |
| ret = g_list_reverse (ret); |
| ret = ks_video_media_type_list_remove_duplicates (ret); |
| } |
| |
| beach: |
| return ret; |
| } |
| |
| KSPIN_CONNECT * |
| ks_video_create_pin_conn_from_media_type (KsVideoMediaType * media_type) |
| { |
| KSPIN_CONNECT *conn = NULL; |
| KSDATAFORMAT *format = NULL; |
| guint8 *vih; |
| |
| conn = g_malloc0 (sizeof (KSPIN_CONNECT) + sizeof (KSDATAFORMAT) + |
| media_type->format_size); |
| |
| conn->Interface.Set = KSINTERFACESETID_Standard; |
| conn->Interface.Id = KSINTERFACE_STANDARD_STREAMING; |
| conn->Interface.Flags = 0; |
| |
| conn->Medium.Set = KSMEDIUMSETID_Standard; |
| conn->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; |
| conn->Medium.Flags = 0; |
| |
| conn->PinId = media_type->pin_id; |
| conn->PinToHandle = NULL; |
| conn->Priority.PriorityClass = KSPRIORITY_NORMAL; |
| conn->Priority.PrioritySubClass = KSPRIORITY_NORMAL; |
| |
| format = (KSDATAFORMAT *) (conn + 1); |
| memcpy (format, media_type->range, sizeof (KSDATAFORMAT)); |
| format->FormatSize = sizeof (KSDATAFORMAT) + media_type->format_size; |
| |
| vih = (guint8 *) (format + 1); |
| memcpy (vih, media_type->format, media_type->format_size); |
| |
| return conn; |
| } |
| |
| gboolean |
| ks_video_fixate_media_type (const KSDATARANGE * range, |
| guint8 * format, gint width, gint height, gint fps_n, gint fps_d) |
| { |
| KS_DATARANGE_VIDEO *vr; |
| KS_VIDEOINFOHEADER *vih; |
| KS_BITMAPINFOHEADER *bih; |
| DWORD dwRate; |
| |
| g_return_val_if_fail (format != NULL, FALSE); |
| |
| if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) { |
| bih = &((KS_VIDEOINFOHEADER *) format)->bmiHeader; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) { |
| bih = &((KS_VIDEOINFOHEADER2 *) format)->bmiHeader; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) { |
| bih = &((KS_MPEG1VIDEOINFO *) format)->hdr.bmiHeader; |
| } else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) { |
| bih = &((KS_MPEGVIDEOINFO2 *) format)->hdr.bmiHeader; |
| } else { |
| return FALSE; |
| } |
| |
| /* These formats' structures share the most basic stuff */ |
| vr = (KS_DATARANGE_VIDEO *) range; |
| vih = (KS_VIDEOINFOHEADER *) format; |
| |
| /* FIXME: Need to figure out how to properly handle ranges */ |
| if (bih->biWidth != width || bih->biHeight != height) |
| return FALSE; |
| |
| /* Framerate, clamped because of fraction conversion rounding errors */ |
| vih->AvgTimePerFrame = |
| gst_util_uint64_scale_int_round (NANOSECONDS, fps_d, fps_n); |
| vih->AvgTimePerFrame = |
| MAX (vih->AvgTimePerFrame, vr->ConfigCaps.MinFrameInterval); |
| vih->AvgTimePerFrame = |
| MIN (vih->AvgTimePerFrame, vr->ConfigCaps.MaxFrameInterval); |
| |
| /* Bitrate, clamped for the same reason as framerate */ |
| dwRate = (width * height * fps_n) / fps_d; |
| vih->dwBitRate = dwRate * bih->biBitCount; |
| vih->dwBitRate = MAX (vih->dwBitRate, vr->ConfigCaps.MinBitsPerSecond); |
| vih->dwBitRate = MIN (vih->dwBitRate, vr->ConfigCaps.MaxBitsPerSecond); |
| |
| return TRUE; |
| } |
| |
| static GstStructure * |
| ks_video_append_var_video_fields (GstStructure * structure) |
| { |
| if (structure) { |
| gst_structure_set (structure, |
| "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
| "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
| "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); |
| } |
| |
| return structure; |
| } |
| |
| GstCaps * |
| ks_video_get_all_caps (void) |
| { |
| static GstCaps *caps = NULL; |
| |
| if (caps == NULL) { |
| GstStructure *structure; |
| caps = gst_caps_new_empty (); |
| |
| /* from Windows SDK 6.0 uuids.h */ |
| /* RGB formats */ |
| structure = |
| ks_video_append_var_video_fields (ks_video_format_to_structure |
| (MEDIASUBTYPE_RGB555, FORMAT_VideoInfo, NULL)); |
| gst_caps_append_structure (caps, structure); |
| |
| structure = |
| ks_video_append_var_video_fields (ks_video_format_to_structure |
| (MEDIASUBTYPE_RGB565, FORMAT_VideoInfo, NULL)); |
| gst_caps_append_structure (caps, structure); |
| |
| structure = |
| ks_video_append_var_video_fields (ks_video_format_to_structure |
| (MEDIASUBTYPE_RGB24, FORMAT_VideoInfo, NULL)); |
| gst_caps_append_structure (caps, structure); |
| |
| structure = |
| ks_video_append_var_video_fields (ks_video_format_to_structure |
| (MEDIASUBTYPE_RGB32, FORMAT_VideoInfo, NULL)); |
| gst_caps_append_structure (caps, structure); |
| |
| /* YUV formats */ |
| structure = |
| ks_video_append_var_video_fields (gst_structure_new_empty |
| ("video/x-raw")); |
| gst_caps_append_structure (caps, structure); |
| |
| /* Other formats */ |
| structure = |
| ks_video_append_var_video_fields (ks_video_format_to_structure |
| (MEDIASUBTYPE_MJPG, FORMAT_VideoInfo, NULL)); |
| gst_caps_append_structure (caps, structure); |
| |
| structure = |
| ks_video_append_var_video_fields (ks_video_format_to_structure |
| (MEDIASUBTYPE_dvsd, FORMAT_VideoInfo, NULL)); |
| gst_caps_append_structure (caps, structure); |
| |
| structure = /* no variable video fields (width, height, framerate) */ |
| ks_video_format_to_structure (MEDIASUBTYPE_dvsd, FORMAT_DvInfo, NULL); |
| gst_caps_append_structure (caps, structure); |
| } |
| |
| return caps; |
| } |