gstdlnasrc.c 130 KB
Newer Older
Brendan Long's avatar
Brendan Long committed
1
/* Copyright (C) 2013 Cable Television Laboratories, Inc.
2
 *
Brendan Long's avatar
Brendan Long committed
3 4 5
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
6
 *
Brendan Long's avatar
Brendan Long committed
7 8 9 10 11
 * 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.
12
 *
Brendan Long's avatar
Brendan Long committed
13 14 15
 * 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
16 17 18 19 20 21 22 23
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CABLE TELEVISION LABS INC. OR ITS
 * 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.
24 25 26 27 28 29
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

30
#include <ctype.h>
31 32
#include <stdlib.h>
#include <string.h>
33
#include <math.h>
34

35 36 37 38 39
#include <stdio.h>
#include <gst/gst.h>
#include <glib-object.h>

#include "gstdlnasrc.h"
40
#include "util.h"
41 42 43

enum
{
44 45 46
  PROP_0,
  PROP_URI,
  PROP_SUPPORTED_RATES,
47
  PROP_DTCP_BLOCKSIZE,
48 49
};

50
#define DEFAULT_DTCP_BLOCKSIZE       524288
51

52
#define ELEMENT_NAME_SOUP_HTTP_SRC "soup-http-source"
53 54
#define ELEMENT_NAME_DTCP_DECRYPTER "dtcp-decrypter"

55
#define MAX_HTTP_BUF_SIZE 2048
56
static const gchar CRLF[] = "\r\n";
57

58
static const gchar COLON[] = ":";
59

60
static const gchar *HEAD_RESPONSE_HEADERS[] = {
61 62 63 64 65 66 67 68 69 70
  "HTTP/",                      /* 0 */
  "VARY",                       /* 1 */
  "TIMESEEKRANGE.DLNA.ORG",     /* 2 */
  "TRANSFERMODE.DLNA.ORG",      /* 3 */
  "DATE",                       /* 4 */
  "CONTENT-TYPE",               /* 5 */
  "SERVER",                     /* 6 */
  "TRANSFER-ENCODING",          /* 7 */
  "CONTENTFEATURES.DLNA.ORG",   /* 8 */
  "CONTENT-RANGE.DTCP.COM",     /* 9 */
71 72 73 74 75 76
  "PRAGMA",                     /* 10 */
  "CACHE-CONTROL",              /* 11 */
  "CONTENT-LENGTH",             /* 12 */
  "ACCEPT-RANGES",              /* 13 */
  "CONTENT-RANGE",              /* 14 */
  "AVAILABLESEEKRANGE.DLNA.ORG" /* 15 */
77 78
};

79 80
/* Constants which represent indices in HEAD_RESPONSE_HEADERS string array
  NOTE: Needs to stay in sync with HEAD_RESPONSE_HEADERS */
81 82 83 84 85 86 87 88 89 90 91 92
#define HEADER_INDEX_HTTP 0
#define HEADER_INDEX_VARY 1
#define HEADER_INDEX_TIMESEEKRANGE 2
#define HEADER_INDEX_TRANSFERMODE 3
#define HEADER_INDEX_DATE 4
#define HEADER_INDEX_CONTENT_TYPE 5
#define HEADER_INDEX_SERVER 6
#define HEADER_INDEX_TRANSFER_ENCODING 7
#define HEADER_INDEX_CONTENTFEATURES 8
#define HEADER_INDEX_DTCP_RANGE 9
#define HEADER_INDEX_PRAGMA 10
#define HEADER_INDEX_CACHE_CONTROL 11
93 94
#define HEADER_INDEX_CONTENT_LENGTH 12
#define HEADER_INDEX_ACCEPT_RANGES 13
95
#define HEADER_INDEX_CONTENT_RANGE 14
96
#define HEADER_INDEX_AVAILABLE_RANGE 15
97

98
/* Count in HEAD_RESPONSE_HEADERS and HEADER_INDEX_* constants */
99
static const gint HEAD_RESPONSE_HEADERS_CNT = 16;
100

101
/* Subfield headers which specify ranges */
102
static const gchar *RANGE_HEADERS[] = {
103 104 105
  "NPT",                        /* 0 */
  "BYTES",                      /* 1 */
  "CLEARTEXTBYTES",             /* 2 */
106
};
107

108 109
#define HEADER_INDEX_NPT 0
#define HEADER_INDEX_BYTES 1
110 111
#define HEADER_INDEX_CLEAR_TEXT 2

112
/* Subfield headers within ACCEPT-RANGES */
113
static const gchar *ACCEPT_RANGES_BYTES = "BYTES";
114

115
/* Subfield headers within CONTENTFEATURES.DLNA.ORG */
116
static const gchar *CONTENT_FEATURES_HEADERS[] = {
117 118 119 120 121
  "DLNA.ORG_PN",                /* 0 */
  "DLNA.ORG_OP",                /* 1 */
  "DLNA.ORG_PS",                /* 2 */
  "DLNA.ORG_FLAGS",             /* 3 */
  "DLNA.ORG_CI",                /* 4 */
122
};
123

124 125 126 127
#define HEADER_INDEX_PN 0
#define HEADER_INDEX_OP 1
#define HEADER_INDEX_PS 2
#define HEADER_INDEX_FLAGS 3
128
#define HEADER_INDEX_CI 4
129

130
/* Subfield headers with CONTENT-TYPE */
131
static const gchar *CONTENT_TYPE_HEADERS[] = {
132 133 134 135
  "DTCP1HOST",                  /* 0 */
  "DTCP1PORT",                  /* 1 */
  "CONTENTFORMAT",              /* 2 */
  "APPLICATION/X-DTCP1"         /* 3 */
136
};
137

138 139 140 141
#define HEADER_INDEX_DTCP_HOST 0
#define HEADER_INDEX_DTCP_PORT 1
#define HEADER_INDEX_CONTENT_FORMAT 2
#define HEADER_INDEX_APP_DTCP 3
142 143


144
/**
145 146 147 148 149
 * DLNA Flag parameters defined in DLNA spec
 * primary flags - 8 hexadecimal digits representing 32 binary flags
 * protocol info dlna org flags represented by primary flags followed
 * by reserved data of 24 hexadecimal digits (zeros)
 */
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
static const gint SP_FLAG = 1 << 31;    /* Sender Paced Flag - content src is clock  */
static const gint LOP_NPT = 1 << 30;    /* Limited Operations Flags: Time-Based Seek */
static const gint LOP_BYTES = 1 << 29;  /* Limited Operations Flags: Byte-Based Seek */
static const gint PLAYCONTAINER_PARAM = 1 << 28;        /* DLNA PlayContainer Flag */
static const gint S0_INCREASING = 1 << 27;      /* UCDAM s0 Increasing Flag - content has no fixed beginning */
static const gint SN_INCREASING = 1 << 26;      /* UCDAM sN Increasing Flag - content has no fixed ending */
static const gint RTSP_PAUSE = 1 << 25; /* Pause media operation support for RTP Serving Endpoints */
static const gint TM_S = 1 << 24;       /* Streaming Mode Flag - av content must have this set */
static const gint TM_I = 1 << 23;       /* Interactive Mode Flag */
static const gint TM_B = 1 << 22;       /* Background Mode Flag */
static const gint HTTP_STALLING = 1 << 21;      /* HTTP Connection Stalling Flag */
static const gint DLNA_V15_FLAG = 1 << 20;      /* DLNA v1.5 versioning flag */
static const gint LP_FLAG = 1 << 16;    /* Link Content Flag */
static const gint CLEARTEXTBYTESEEK_FULL_FLAG = 1 << 15;        /* Support for Full RADA ClearTextByteSeek header */
static const gint LOP_CLEARTEXTBYTES = 1 << 14; /* Support for Limited RADA ClearTextByteSeek header */
165 166 167

static const int RESERVED_FLAGS_LENGTH = 24;

168 169 170
#define HTTP_STATUS_OK 200
#define HTTP_STATUS_CREATED 201
#define HTTP_STATUS_PARTIAL 206
171
#define HTTP_STATUS_BAD_REQUEST 400
172
#define HTTP_STATUS_NOT_ACCEPTABLE 406
173

174 175 176 177 178 179 180 181 182 183 184 185 186 187
#define HEADER_GET_CONTENT_FEATURES_TITLE "getcontentFeatures.dlna.org"
#define HEADER_GET_CONTENT_FEATURES_VALUE "1"

#define HEADER_GET_AVAILABLE_SEEK_RANGE_TITLE "getAvailableSeekRange.dlna.org"
#define HEADER_GET_AVAILABLE_SEEK_RANGE_VALUE "1"

#define HEADER_RANGE_BYTES_TITLE "Range"
#define HEADER_RANGE_BYTES_VALUE "bytes=0-"

#define HEADER_DTCP_RANGE_BYTES_TITLE "Range.dtcp.com"
#define HEADER_DTCP_RANGE_BYTES_VALUE "bytes=0-"

#define HEADER_TIME_SEEK_RANGE_TITLE "TimeSeekRange.dlna.org"
#define HEADER_TIME_SEEK_RANGE_VALUE "npt=0-"
188

189 190 191 192 193
static GstStaticPadTemplate gst_dlna_src_pad_template =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("ANY")
194
    );
195

196
static void gst_dlna_src_finalize (GObject * object);
197

198 199
static void gst_dlna_src_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * spec);
200

201 202
static void gst_dlna_src_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * spec);
203

204 205
static gboolean gst_dlna_src_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
206

207 208
static gboolean gst_dlna_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query);
209

210 211 212
static GstStateChangeReturn gst_dlna_src_change_state (GstElement * element,
    GstStateChange transition);

213
static void gst_dlna_src_uri_handler_init (gpointer g_iface,
214
    gpointer iface_data);
215

216 217 218
static gboolean dlna_src_uri_assign (GstDlnaSrc * dlna_src, const gchar * uri,
    GError ** error);

219
static gboolean dlna_src_uri_init (GstDlnaSrc * dlna_src, GError ** error);
220

221 222
static gboolean dlna_src_uri_gather_info (GstDlnaSrc * dlna_src,
    GError ** error);
223

224
static gboolean dlna_src_setup_bin (GstDlnaSrc * dlna_src, GError ** error);
225

226
static gboolean dlna_src_setup_dtcp (GstDlnaSrc * dlna_src);
227

228 229 230 231 232 233 234
static gboolean dlna_src_soup_session_open (GstDlnaSrc * dlna_src);
static void dlna_src_soup_session_close (GstDlnaSrc * dlna_src);
static void dlna_src_soup_log_msg (GstDlnaSrc * dlna_src);
static gboolean
dlna_src_soup_issue_head (GstDlnaSrc * dlna_src, gsize header_array_size,
    gchar * headers[][2], GstDlnaSrcHeadResponse * head_response,
    gboolean do_update_overall_info);
235

236
static gboolean dlna_src_head_response_parse (GstDlnaSrc * dlna_src,
237
    GstDlnaSrcHeadResponse * head_response);
238

239
static gint dlna_src_head_response_get_field_idx (GstDlnaSrc * dlna_src,
240
    const gchar * field_str);
241

242
static gboolean dlna_src_head_response_assign_field_value (GstDlnaSrc *
243
    dlna_src, GstDlnaSrcHeadResponse * head_response, gint idx,
244
    const gchar * field_value);
245

246
static gboolean dlna_src_head_response_parse_time_seek (GstDlnaSrc * dlna_src,
247
    GstDlnaSrcHeadResponse * head_response, gint idx, const gchar * field_str);
248

249 250
static gboolean
dlna_src_head_response_parse_available_range (GstDlnaSrc * dlna_src,
251
    GstDlnaSrcHeadResponse * head_response, gint idx, const gchar * field_str);
252

253
static gboolean dlna_src_head_response_parse_content_features (GstDlnaSrc *
254
    dlna_src, GstDlnaSrcHeadResponse * head_response, gint idx,
255
    const gchar * field_str);
256

257
static gboolean dlna_src_head_response_parse_profile (GstDlnaSrc * dlna_src,
258
    GstDlnaSrcHeadResponse * head_response, gint idx, const gchar * field_str);
259

260
static gboolean dlna_src_head_response_parse_operations (GstDlnaSrc *
261
    dlna_src, GstDlnaSrcHeadResponse * head_response, gint idx,
262
    const gchar * field_str);
263

264
static gboolean dlna_src_head_response_parse_playspeeds (GstDlnaSrc *
265
    dlna_src, GstDlnaSrcHeadResponse * head_response, gint idx,
266
    const gchar * field_str);
267

268
static gboolean dlna_src_head_response_parse_flags (GstDlnaSrc * dlna_src,
269
    GstDlnaSrcHeadResponse * head_response, gint idx, const gchar * field_str);
270

271 272 273 274
static gboolean
dlna_src_head_response_parse_conversion_indicator (GstDlnaSrc * dlna_src,
    GstDlnaSrcHeadResponse * head_response, gint idx, const gchar * field_str);

275
static gboolean dlna_src_head_response_parse_content_type (GstDlnaSrc *
276
    dlna_src, GstDlnaSrcHeadResponse * head_response, gint idx,
277
    const gchar * field_str);
278

279
static gboolean dlna_src_head_response_is_flag_set (GstDlnaSrc * dlna_src,
280 281 282 283
    const gchar * flags_str, gint flag);

static gboolean dlna_src_update_overall_info (GstDlnaSrc * dlna_src,
    GstDlnaSrcHeadResponse * head_response);
284

285 286
static gboolean dlna_src_head_response_init_struct (GstDlnaSrc * dlna_src,
    GstDlnaSrcHeadResponse ** head_response);
287

288 289 290
static void dlna_src_head_response_free_struct (GstDlnaSrc * dlna_src,
    GstDlnaSrcHeadResponse * head_response);

291 292
static void dlna_src_head_response_struct_to_str (GstDlnaSrc * dlna_src,
    GstDlnaSrcHeadResponse * head_response, GString * struct_str);
293

294 295
static void dlna_src_struct_append_header_value_str (GString * struct_str,
    gchar * title, gchar * value);
296

297 298
static void dlna_src_struct_append_header_value_bool (GString * struct_str,
    gchar * title, gboolean value);
299

300 301
static void dlna_src_struct_append_header_value_guint (GString * struct_str,
    gchar * title, guint value);
302

303 304
static void dlna_src_struct_append_header_value_guint64 (GString * struct_str,
    gchar * title, guint64 value);
305

306 307
static void dlna_src_struct_append_header_value_str_guint64 (GString *
    struct_str, gchar * title, gchar * value_str, guint64 value);
308

309 310
static gboolean dlna_src_handle_event_seek (GstDlnaSrc * dlna_src,
    GstPad * pad, GstEvent * event);
311

312 313
static gboolean dlna_src_handle_query_duration (GstDlnaSrc * dlna_src,
    GstQuery * query);
314

315 316
static gboolean dlna_src_handle_query_seeking (GstDlnaSrc * dlna_src,
    GstQuery * query);
317

318 319
static gboolean dlna_src_handle_query_segment (GstDlnaSrc * dlna_src,
    GstQuery * query);
320

321 322
static gboolean dlna_src_handle_query_convert (GstDlnaSrc * dlna_src,
    GstQuery * query);
323

324
static gboolean dlna_src_parse_byte_range (GstDlnaSrc * dlna_src,
325
    const gchar * field_str, gint header_idx, guint64 * start_byte,
326 327
    guint64 * end_byte, guint64 * total_bytes);

328 329 330
static gboolean dlna_src_is_change_valid (GstDlnaSrc * dlna_src, gfloat rate,
    GstFormat format, guint64 start,
    GstSeekType start_type, guint64 stop, GstSeekType stop_type);
331

332
static gboolean dlna_src_is_rate_supported (GstDlnaSrc * dlna_src, gfloat rate);
333

334
static gboolean dlna_src_adjust_http_src_headers (GstDlnaSrc * dlna_src,
335 336
    gfloat rate, GstFormat format, guint64 start, guint64 stop,
    guint32 new_seqnum);
337

338 339 340 341 342 343 344
static gboolean
dlna_src_convert_bytes_to_npt_nanos (GstDlnaSrc * dlna_src, guint64 bytes,
    guint64 * npt_nanos);

static gboolean
dlna_src_convert_npt_nanos_to_bytes (GstDlnaSrc * dlna_src, guint64 npt_nanos,
    guint64 * bytes);
345

346 347 348 349
static void
dlna_src_nanos_to_npt (GstDlnaSrc * dlna_src, guint64 npt_nanos,
    GString * npt_str);

350
#define gst_dlna_src_parent_class parent_class
351

352
G_DEFINE_TYPE_WITH_CODE (GstDlnaSrc, gst_dlna_src, GST_TYPE_BIN,
353 354
    G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
        gst_dlna_src_uri_handler_init));
355 356 357

GST_DEBUG_CATEGORY_STATIC (gst_dlna_src_debug);
#define GST_CAT_DEFAULT gst_dlna_src_debug
358 359 360 361 362 363 364 365 366 367 368 369 370

/*
 * Initializes (only called once) the class associated with this element from within
 * gstreamer framework.  Installs properties and assigns specific
 * methods for function pointers.  Also defines detailed info
 * associated with this element.  The purpose of the *_class_init
 * method is to register the plugin with the GObject system.
 *
 * @param	klass	class representation of this element
 */
static void
gst_dlna_src_class_init (GstDlnaSrcClass * klass)
{
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
  GObjectClass *gobject_klass;

  gobject_klass = (GObjectClass *) klass;

  GstElementClass *gstelement_klass;
  gstelement_klass = (GstElementClass *) klass;
  gst_element_class_set_static_metadata (gstelement_klass,
      "HTTP/DLNA client source 2/20/13 7:37 AM",
      "Source/Network",
      "Receive data as a client via HTTP with DLNA extensions",
      "Eric Winkelman <e.winkelman@cablelabs.com>");

  gst_element_class_add_pad_template (gstelement_klass,
      gst_static_pad_template_get (&gst_dlna_src_pad_template));

  gobject_klass->set_property = gst_dlna_src_set_property;
  gobject_klass->get_property = gst_dlna_src_get_property;

  g_object_class_install_property (gobject_klass, PROP_URI,
      g_param_spec_string ("uri", "Stream URI",
Brendan Long's avatar
Brendan Long committed
391 392
          "Sets URI A/V stream", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
393 394

  g_object_class_install_property (gobject_klass, PROP_SUPPORTED_RATES,
395
      g_param_spec_boxed ("supported-rates",
396
          "Supported Playspeed rates",
397
          "List of supported playspeed rates of DLNA server content",
398
          G_TYPE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
399

400
  g_object_class_install_property (gobject_klass, PROP_DTCP_BLOCKSIZE,
401
      g_param_spec_uint ("dtcp-blocksize", "DTCP Block size",
402
          "Size in bytes to read per buffer when content is dtcp encrypted (-1 = default)",
Brendan Long's avatar
Brendan Long committed
403 404
          0, G_MAXUINT, DEFAULT_DTCP_BLOCKSIZE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
405

406
  gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_dlna_src_finalize);
407
  gstelement_klass->change_state = gst_dlna_src_change_state;
408 409 410 411 412 413 414 415 416
}

/*
 * Initializes a specific instance of this element, called when object
 * is created from within gstreamer framework.
 *
 * @param dlna_src	specific instance of element to intialize
 */
static void
417
gst_dlna_src_init (GstDlnaSrc * dlna_src)
418
{
419 420 421 422 423
  GST_INFO_OBJECT (dlna_src, "Initializing");

  dlna_src->http_src = NULL;
  dlna_src->dtcp_decrypter = NULL;
  dlna_src->src_pad = NULL;
424

425
  dlna_src->dtcp_blocksize = DEFAULT_DTCP_BLOCKSIZE;
426
  dlna_src->src_pad = NULL;
427
  dlna_src->dtcp_key_storage = NULL;
428 429 430
  dlna_src->uri = NULL;
  dlna_src->soup_session = NULL;
  dlna_src->soup_msg = NULL;
431

432 433 434 435 436 437 438 439 440 441 442 443
  dlna_src->server_info = NULL;

  dlna_src->rate = 1.0;
  dlna_src->requested_rate = 1.0;
  dlna_src->requested_format = GST_FORMAT_BYTES;
  dlna_src->requested_start = 0;
  dlna_src->requested_stop = -1;

  dlna_src->time_seek_seqnum = 0;
  dlna_src->time_seek_event_start = 0;
  dlna_src->handled_time_seek_seqnum = FALSE;

444 445 446 447 448 449 450 451 452
  dlna_src->is_live = FALSE;
  dlna_src->is_encrypted = FALSE;

  dlna_src->byte_seek_supported = FALSE;
  dlna_src->byte_start = 0;
  dlna_src->byte_end = 0;
  dlna_src->byte_total = 0;

  dlna_src->time_seek_supported = FALSE;
453 454
  dlna_src->npt_start_nanos = GST_CLOCK_TIME_NONE;
  dlna_src->npt_end_nanos = GST_CLOCK_TIME_NONE;
455 456 457 458 459
  dlna_src->npt_duration_nanos = 0;
  dlna_src->npt_start_str = NULL;
  dlna_src->npt_end_str = NULL;
  dlna_src->npt_duration_str = NULL;

460
  GST_LOG_OBJECT (dlna_src, "Initialization complete");
461 462 463 464 465 466 467 468
}

/**
 * Called by framework when tearing down pipeline
 *
 * @param object  element to destroy
 */
static void
469
gst_dlna_src_finalize (GObject * object)
470
{
471
  GstDlnaSrc *dlna_src = GST_DLNA_SRC (object);
472

473
  GST_INFO_OBJECT (dlna_src, " Disposing the dlna src");
474

475 476 477 478
  dlna_src_soup_session_close (dlna_src);

  dlna_src_head_response_free_struct (dlna_src, dlna_src->server_info);

479
  g_free (dlna_src->dtcp_key_storage);
480
  g_free (dlna_src->uri);
481 482
  if (dlna_src->soup_msg)
    g_object_unref (dlna_src->soup_msg);
483 484

  G_OBJECT_CLASS (parent_class)->finalize (object);
485 486 487 488 489 490 491
}

/**
 * Method called by framework to set this element's properties
 *
 * @param	object	set property of this element
 * @param	prop_id	identifier of property to set
492 493
 * @param	value	set property to this value
 * @param	pspec	description of property type
494
 */
495
static void
496
gst_dlna_src_set_property (GObject * object, guint prop_id,
497
    const GValue * value, GParamSpec * pspec)
498
{
499
  GstDlnaSrc *dlna_src = GST_DLNA_SRC (object);
500

501
  GST_INFO_OBJECT (dlna_src, "Setting property: %d", prop_id);
502

503
  switch (prop_id) {
504

Lori Anderson's avatar
Lori Anderson committed
505 506
    case PROP_URI:
    {
507
      if (!dlna_src_uri_assign (dlna_src, g_value_get_string (value), NULL)) {
508 509 510 511 512
        GST_ELEMENT_ERROR (dlna_src, RESOURCE, READ,
            ("%s() - unable to set URI: %s",
                __FUNCTION__, g_value_get_string (value)), NULL);
      }
      break;
Lori Anderson's avatar
Lori Anderson committed
513
    }
514 515 516 517 518
    case PROP_DTCP_BLOCKSIZE:
      dlna_src->dtcp_blocksize = g_value_get_uint (value);
      GST_INFO_OBJECT (dlna_src, "Set DTCP blocksize: %d",
          dlna_src->dtcp_blocksize);
      break;
Lori Anderson's avatar
Lori Anderson committed
519
    default:
520 521 522
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
523 524 525 526 527 528 529 530 531 532 533 534
}

/**
 * Retrieves current value of property associated with supplied element instance.
 *
 * @param	object	get property value of this element
 * @param	prop_id	get property identified by this supplied id
 * @param	value	returned current value of property
 * @param	pspec	description of property type
 */
static void
gst_dlna_src_get_property (GObject * object, guint prop_id, GValue * value,
535
    GParamSpec * pspec)
536
{
537
  GstDlnaSrc *dlna_src = GST_DLNA_SRC (object);
538

539
  int i = 0;
540
  GArray *garray = NULL;
541 542 543 544
  gfloat rate = 0;
  int psCnt = 0;

  switch (prop_id) {
545

Lori Anderson's avatar
Lori Anderson committed
546
    case PROP_URI:
547
      GST_LOG_OBJECT (dlna_src, "Getting property: uri");
548 549
      if (dlna_src->uri != NULL) {
        g_value_set_string (value, dlna_src->uri);
550
        GST_LOG_OBJECT (dlna_src, "Get property returning: %s",
551 552 553
            g_value_get_string (value));
      }
      break;
554

Lori Anderson's avatar
Lori Anderson committed
555
    case PROP_SUPPORTED_RATES:
556
      GST_LOG_OBJECT (dlna_src, "Getting property: supported rates");
557 558 559 560
      if ((dlna_src->server_info != NULL) &&
          (dlna_src->server_info->content_features != NULL) &&
          (dlna_src->server_info->content_features->playspeeds_cnt > 0)) {
        psCnt = dlna_src->server_info->content_features->playspeeds_cnt;
561 562
        garray = g_array_sized_new (TRUE, TRUE, sizeof (gfloat), psCnt);
        for (i = 0; i < psCnt; i++) {
563
          rate = dlna_src->server_info->content_features->playspeeds[i];
564
          g_array_append_val (garray, rate);
565
          GST_LOG_OBJECT (dlna_src, "Rate %d: %f", (i + 1),
566 567 568
              g_array_index (garray, gfloat, i));
        }

569
        memset (value, 0, sizeof (*value));
570 571 572 573
        g_value_init (value, G_TYPE_ARRAY);
        g_value_take_boxed (value, garray);
      }
      break;
Lori Anderson's avatar
Lori Anderson committed
574

575 576 577 578
    case PROP_DTCP_BLOCKSIZE:
      g_value_set_uint (value, dlna_src->dtcp_blocksize);
      break;

Lori Anderson's avatar
Lori Anderson committed
579
    default:
580 581
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  }
582 583
}

584 585 586 587 588 589
static GstStateChangeReturn
gst_dlna_src_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  GstDlnaSrc *dlna_src = GST_DLNA_SRC (element);

590
  GST_LOG_OBJECT (dlna_src, "Changing state from %s to %s",
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      break;
    default:
      break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  if (ret == GST_STATE_CHANGE_FAILURE) {
    GST_ERROR_OBJECT (dlna_src, "Problems with parent class state change");
    return ret;
  }

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
      break;
    default:
      break;
  }

  return ret;
}

621
/**
622
 * Processes the supplied event
623
 *
624 625 626
 * @param pad	    pad where event was received
 * @param parent    parent of pad
 * @param event	    received event
627
 *
628
 * @return	true if event was handled, false otherwise
629
 */
630
static gboolean
631
gst_dlna_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
632
{
633 634
  gboolean ret = FALSE;
  GstDlnaSrc *dlna_src = GST_DLNA_SRC (gst_pad_get_parent (pad));
635

636
  switch (GST_EVENT_TYPE (event)) {
Lori Anderson's avatar
Lori Anderson committed
637
    case GST_EVENT_SEEK:
638 639 640 641
      GST_INFO_OBJECT (dlna_src, "Got src event: %s",
          GST_EVENT_TYPE_NAME (event));
      ret = dlna_src_handle_event_seek (dlna_src, pad, event);
      break;
Lori Anderson's avatar
Lori Anderson committed
642

Lori Anderson's avatar
Lori Anderson committed
643
    case GST_EVENT_FLUSH_START:
644 645
      GST_DEBUG_OBJECT (dlna_src, "Got src event: %s",
          GST_EVENT_TYPE_NAME (event));
646
      break;
Lori Anderson's avatar
Lori Anderson committed
647 648

    case GST_EVENT_FLUSH_STOP:
649 650 651
      GST_DEBUG_OBJECT (dlna_src, "Got src event: %s",
          GST_EVENT_TYPE_NAME (event));
      break;
Lori Anderson's avatar
Lori Anderson committed
652 653 654 655 656

    case GST_EVENT_QOS:
    case GST_EVENT_LATENCY:
    case GST_EVENT_NAVIGATION:
    case GST_EVENT_RECONFIGURE:
657
      /* Just call default handler to handle */
658
      break;
Lori Anderson's avatar
Lori Anderson committed
659 660

    default:
661 662 663 664
      GST_DEBUG_OBJECT (dlna_src, "Unsupported event: %s",
          GST_EVENT_TYPE_NAME (event));
      break;
  }
Lori Anderson's avatar
Lori Anderson committed
665

666
  /* If not handled, pass on to default pad handler */
667 668 669
  if (!ret) {
    ret = gst_pad_event_default (pad, parent, event);
  }
Lori Anderson's avatar
Lori Anderson committed
670

671
  return ret;
672 673
}

674
/**
675
 * Handles queries on the pad
676
 *
677 678 679 680 681
 * @param pad       pad where query was received
 * @param parent    parent of pad
 * @param event     received query
 *
 * @return true if query could be performed, false otherwise
682 683
 */
static gboolean
684
gst_dlna_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
685
{
686 687
  gboolean ret = FALSE;
  GstDlnaSrc *dlna_src = GST_DLNA_SRC (gst_pad_get_parent (pad));
688

689
  GST_LOG_OBJECT (dlna_src, "Got src query: %s", GST_QUERY_TYPE_NAME (query));
Lori Anderson's avatar
Lori Anderson committed
690

691
  switch (GST_QUERY_TYPE (query)) {
Lori Anderson's avatar
Lori Anderson committed
692
    case GST_QUERY_DURATION:
693 694
      ret = dlna_src_handle_query_duration (dlna_src, query);
      break;
695

Lori Anderson's avatar
Lori Anderson committed
696
    case GST_QUERY_SEEKING:
697 698
      ret = dlna_src_handle_query_seeking (dlna_src, query);
      break;
699

Lori Anderson's avatar
Lori Anderson committed
700
    case GST_QUERY_SEGMENT:
701 702
      ret = dlna_src_handle_query_segment (dlna_src, query);
      break;
703

Lori Anderson's avatar
Lori Anderson committed
704
    case GST_QUERY_CONVERT:
705 706
      ret = dlna_src_handle_query_convert (dlna_src, query);
      break;
707

Lori Anderson's avatar
Lori Anderson committed
708
    case GST_QUERY_URI:
709 710 711 712
      GST_INFO_OBJECT (dlna_src, "query uri");
      gst_query_set_uri (query, dlna_src->uri);
      ret = TRUE;
      break;
713

Lori Anderson's avatar
Lori Anderson committed
714
    case GST_QUERY_FORMATS:
715
      GST_INFO_OBJECT (dlna_src, "format query");
716
      gst_query_set_formats (query, 2, GST_FORMAT_BYTES, GST_FORMAT_TIME);
717 718
      ret = TRUE;
      break;
719

Lori Anderson's avatar
Lori Anderson committed
720
    case GST_QUERY_LATENCY:
721
      /* Don't know latency, let some other element handle this */
722
      break;
Lori Anderson's avatar
Lori Anderson committed
723

Lori Anderson's avatar
Lori Anderson committed
724
    case GST_QUERY_POSITION:
725
      /* Don't know current position in stream, let some other element handle this */
726
      break;
Lori Anderson's avatar
Lori Anderson committed
727

Lori Anderson's avatar
Lori Anderson committed
728
    default:
729 730 731 732 733 734 735 736 737 738 739
      GST_LOG_OBJECT (dlna_src,
          "Got unsupported src query: %s, passing to default handler",
          GST_QUERY_TYPE_NAME (query));
      break;
  }

  if (!ret) {
    ret = gst_pad_query_default (pad, parent, query);
  }

  return ret;
740 741
}

742 743 744 745 746 747 748 749
/**
 * Responds to a duration query by returning the size of content/stream
 *
 * @param	dlna_src	this element
 * @param	query		received duration query to respond to
 *
 * @return	true if responded to query, false otherwise
 */
750 751
static gboolean
dlna_src_handle_query_duration (GstDlnaSrc * dlna_src, GstQuery * query)
752
{
753 754 755
  gboolean ret = FALSE;
  gint64 duration = 0;
  GstFormat format;
756

757
  GST_LOG_OBJECT (dlna_src, "Called");
758

759
  if ((dlna_src->uri == NULL) || (dlna_src->server_info == NULL)) {
760
    GST_INFO_OBJECT (dlna_src,
761 762 763
        "No URI and/or HEAD response info, unable to handle query");
    return FALSE;
  }
764

765 766
  gst_query_parse_duration (query, &format, &duration);

767
  if (format == GST_FORMAT_BYTES) {
768 769
    if (dlna_src->byte_total) {
      gst_query_set_duration (query, GST_FORMAT_BYTES, dlna_src->byte_total);
770
      ret = TRUE;
771
      GST_DEBUG_OBJECT (dlna_src,
772
          "Duration in bytes for this content on the server: %"
773 774 775 776
          G_GUINT64_FORMAT, dlna_src->byte_total);
    } else
      GST_DEBUG_OBJECT (dlna_src,
          "Duration in bytes not available for content item");
777
  } else if (format == GST_FORMAT_TIME) {
778
    if (dlna_src->npt_duration_nanos) {
779
      gst_query_set_duration (query, GST_FORMAT_TIME,
780
          dlna_src->npt_duration_nanos);
781
      ret = TRUE;
782
      GST_DEBUG_OBJECT (dlna_src,
783 784 785 786 787
          "Duration in media time for this content on the server, npt: %"
          GST_TIME_FORMAT ", nanosecs: %" G_GUINT64_FORMAT,
          GST_TIME_ARGS (dlna_src->npt_duration_nanos),
          dlna_src->npt_duration_nanos);
    } else
788
      GST_DEBUG_OBJECT (dlna_src,
789 790
          "Duration in media time not available for content item");
  } else {
791
    GST_DEBUG_OBJECT (dlna_src,
792 793 794 795
        "Got duration query with non-supported format type: %s, passing to default handler",
        gst_format_get_name (format));
  }
  return ret;
796 797
}

798 799 800 801 802 803 804 805
/**
 * Responds to a seeking query by returning whether or not seeking is supported
 *
 * @param	dlna_src	this element
 * @param	query		received seeking query to respond to
 *
 * @return	true if responded to query, false otherwise
 */
806 807
static gboolean
dlna_src_handle_query_seeking (GstDlnaSrc * dlna_src, GstQuery * query)
808
{
809 810 811 812 813 814 815 816
  gboolean ret = FALSE;
  GstFormat format;
  gboolean supports_seeking = FALSE;
  gint64 seek_start = 0;
  gint64 seek_end = 0;

  GST_DEBUG_OBJECT (dlna_src, "Called");

817
  if ((dlna_src->uri == NULL) || (dlna_src->server_info == NULL)) {
818
    GST_INFO_OBJECT (dlna_src,
819 820 821
        "No URI and/or HEAD response info, unable to handle query");
    return FALSE;
  }
822

823 824 825
  gst_query_parse_seeking (query, &format, &supports_seeking, &seek_start,
      &seek_end);

826
  if (format == GST_FORMAT_BYTES) {
827 828 829
    if (dlna_src->byte_seek_supported) {
      gst_query_set_seeking (query, GST_FORMAT_BYTES, TRUE,
          dlna_src->byte_start, dlna_src->byte_end);
830 831
      ret = TRUE;

832
      GST_INFO_OBJECT (dlna_src,
833 834
          "Byte seeks supported for this content by the server, start %"
          G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT,
835
          dlna_src->byte_start, dlna_src->byte_end);
836

837 838
    } else {

839 840
      GST_INFO_OBJECT (dlna_src,
          "Seeking in bytes not available for content item");
841

842 843
      gst_query_set_seeking (query, GST_FORMAT_BYTES, FALSE, seek_start,
          seek_end);
844 845
      ret = TRUE;
    }
846
  } else if (format == GST_FORMAT_TIME) {
847
    if (dlna_src->time_seek_supported) {
848
      gst_query_set_seeking (query, GST_FORMAT_TIME, TRUE,
849
          dlna_src->npt_start_nanos, dlna_src->npt_end_nanos);
850 851
      ret = TRUE;

852
      GST_DEBUG_OBJECT (dlna_src,
853
          "Time based seeks supported for this content by the server, start %"
854
          GST_TIME_FORMAT ", end %" GST_TIME_FORMAT,
855 856
          GST_TIME_ARGS (dlna_src->npt_start_nanos),
          GST_TIME_ARGS (dlna_src->npt_end_nanos));
857 858
    } else {

859
      GST_DEBUG_OBJECT (dlna_src,
860
          "Seeking in media time not available for content item");
861

862 863
      gst_query_set_seeking (query, GST_FORMAT_TIME, FALSE, seek_start,
          seek_end);
864 865
      ret = TRUE;
    }
866
  } else {
867
    GST_DEBUG_OBJECT (dlna_src,
868 869 870
        "Got seeking query with non-supported format type: %s, passing to default handler",
        GST_QUERY_TYPE_NAME (query));
  }
871

872
  return ret;
873 874
}

875 876 877 878 879 880 881 882
/**
 * Responds to a segment query by returning rate along with start and stop
 *
 * @param	dlna_src	this element
 * @param	query		received segment query to respond to
 *
 * @return	true if responded to query, false otherwise
 */
883 884
static gboolean
dlna_src_handle_query_segment (GstDlnaSrc * dlna_src, GstQuery * query)
885
{
886 887 888 889 890 891 892 893
  gboolean ret = FALSE;
  GstFormat format;
  gdouble rate = 1.0;
  gint64 start = 0;
  gint64 end = 0;

  GST_LOG_OBJECT (dlna_src, "Called");

894
  if ((dlna_src->uri == NULL) || (dlna_src->server_info == NULL)) {
895
    GST_INFO_OBJECT (dlna_src,
896 897 898 899 900 901
        "No URI and/or HEAD response info, unable to handle query");
    return FALSE;
  }
  gst_query_parse_segment (query, &rate, &format, &start, &end);

  if (format == GST_FORMAT_BYTES) {
902
    if (dlna_src->byte_seek_supported) {
903

904 905
      gst_query_set_segment (query, dlna_src->rate, GST_FORMAT_BYTES,
          dlna_src->byte_start, dlna_src->byte_end);
906 907
      ret = TRUE;

908
      GST_DEBUG_OBJECT (dlna_src,
909 910
          "Segment info in bytes for this content, rate %f, start %"
          G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT,
911 912 913 914
          dlna_src->rate, dlna_src->byte_start, dlna_src->byte_end);
    } else
      GST_DEBUG_OBJECT (dlna_src,
          "Segment info in bytes not available for content item");
915
  } else if (format == GST_FORMAT_TIME) {
916

917
    if (dlna_src->time_seek_supported) {
918
      gst_query_set_segment (query, dlna_src->rate, GST_FORMAT_TIME,
919
          dlna_src->npt_start_nanos, dlna_src->npt_end_nanos);
920 921
      ret = TRUE;

922
      GST_DEBUG_OBJECT (dlna_src,
923
          "Time based segment info for this content by the server, rate %f, start %"
924
          GST_TIME_FORMAT ", end %" GST_TIME_FORMAT,
925
          dlna_src->rate,
926 927 928
          GST_TIME_ARGS (dlna_src->npt_start_nanos),
          GST_TIME_ARGS (dlna_src->npt_end_nanos));
    } else
929
      GST_DEBUG_OBJECT (dlna_src,
930 931
          "Segment info in media time not available for content item");
  } else {
932
    GST_DEBUG_OBJECT (dlna_src,
933 934 935
        "Got segment query with non-supported format type: %s, passing to default handler",
        GST_QUERY_TYPE_NAME (query));
  }
936

937
  return ret;
938 939
}

940 941 942 943 944 945 946 947
/**
 * Responds to a convert query by issuing head and returning conversion value
 *
 * @param	dlna_src	this element
 * @param	query		received query to respond to
 *
 * @return	true if responded to query, false otherwise
 */
948 949
static gboolean
dlna_src_handle_query_convert (GstDlnaSrc * dlna_src, GstQuery * query)
950
{
951
  /* Always return true since no other element can do this */
952 953 954
  gboolean ret = TRUE;
  GstFormat src_fmt, dest_fmt;
  gint64 src_val, dest_val;
955 956
  gint64 start_byte = 0;
  gint64 start_npt = 0;
957 958 959

  GST_LOG_OBJECT (dlna_src, "Called");

960
  if (dlna_src->uri == NULL || !dlna_src->time_seek_supported) {
961
    GST_INFO_OBJECT (dlna_src, "Not enough info to handle conversion query");
962 963
    return FALSE;
  }