static void
send_chunked_file (SoupServer * server, SoupMessage * message,
		   DAAPRecord * record, guint64 filesize, guint64 offset,
		   const gchar * transcode_mimetype)
{
	gchar *format = NULL;
	gchar *location = NULL;
	GInputStream *stream = NULL;
	gboolean has_video;
	GError *error = NULL;
	ChunkData *cd = NULL;

	cd = g_new (ChunkData, 1);
	if (NULL == cd) {
		g_warning ("Error allocating chunk\n");
		goto _error;
	}

	g_object_get (record, "location", &location, "has-video", &has_video, NULL);
	if (NULL == location) {
		g_warning ("Error getting location from record\n");
		goto _error;
	}

	/* FIXME: This crashes on powerpc-440fp-linux-gnu:
	 * g_debug ("Sending %s chunked from offset %" G_GUINT64_FORMAT ".", location, offset);
	 */

	cd->server = server;

	stream = G_INPUT_STREAM (daap_record_read (record, &error));
	if (error != NULL) {
		g_warning ("Couldn't open %s: %s.", location, error->message);
		goto _error;
	}

	g_object_get (record, "format", &format, NULL);
	if (NULL == format) {
		g_warning ("Error getting format from record\n");
		goto _error;
	}

	// Not presently transcoding videos (see also same comments elsewhere).
	if (should_transcode (format, has_video, transcode_mimetype)) {
#ifdef HAVE_GSTREAMERAPP
		cd->stream = dmap_gst_input_stream_new (transcode_mimetype, stream);
#else
		g_warning ("Transcode format %s not supported", transcode_mimetype);
		cd->stream = stream;
#endif /* HAVE_GSTREAMERAPP */
	} else {
		g_debug ("Not transcoding %s", location);
		cd->stream = stream;
	}

	if (cd->stream == NULL) {
		g_warning ("Could not set up input stream");
		goto _error;
	}

	if (offset != 0) {
		if (g_seekable_seek (G_SEEKABLE (cd->stream), offset, G_SEEK_SET, NULL, &error) == FALSE) {
			g_warning ("Error seeking: %s.", error->message);
			goto _error;
		}
		filesize -= offset;
	}

	/* Free memory after each chunk sent out over network. */
	soup_message_body_set_accumulate (message->response_body, FALSE);

	if (! should_transcode (format, has_video, transcode_mimetype)) {
	        /* NOTE: iTunes seems to require this or it stops reading 
	         * video data after about 2.5MB. Perhaps this is so iTunes
	         * knows how much data to buffer.
	         */
		g_debug ("Using HTTP 1.1 content length encoding.");
		soup_message_headers_set_encoding (message->response_headers, SOUP_ENCODING_CONTENT_LENGTH);

	        /* NOTE: iTunes 8 (and other versions?) will not seek
	         * properly without a Content-Length header.
	         */
		g_debug ("Content length is %" G_GUINT64_FORMAT ".", filesize);
		soup_message_headers_set_content_length (message->response_headers, filesize);
	} else if (soup_message_get_http_version (message) == SOUP_HTTP_1_0) {
		/* NOTE: Roku clients support only HTTP 1.0. */
#ifdef HAVE_ENCODING_EOF
		g_debug ("Using HTTP 1.0 encoding.");
		soup_message_headers_set_encoding (message->response_headers, SOUP_ENCODING_EOF);
#else
		g_warning ("Received HTTP 1.0 request, but not built with HTTP 1.0 support");
		soup_message_headers_set_encoding (message->response_headers, SOUP_ENCODING_CHUNKED);
#endif
	} else {
		/* NOTE: Can not provide Content-Length when performing
		 * real-time transcoding.
		 */
		g_debug ("Using HTTP 1.1 chunked encoding.");
		soup_message_headers_set_encoding (message->response_headers, SOUP_ENCODING_CHUNKED);
	}

	soup_message_headers_append (message->response_headers, "Connection",
				     "Close");
	soup_message_headers_append (message->response_headers,
				     "Content-Type",
				     "application/x-dmap-tagged");

	g_signal_connect (message, "wrote_headers",
			  G_CALLBACK (dmap_write_next_chunk), cd);
	g_signal_connect (message, "wrote_chunk",
			  G_CALLBACK (dmap_write_next_chunk), cd);
	g_signal_connect (message, "finished",
			  G_CALLBACK (dmap_chunked_message_finished), cd);
	/* NOTE: cd g_free'd by chunked_message_finished(). */

	return;
_error:
	soup_message_set_status (message, SOUP_STATUS_INTERNAL_SERVER_ERROR);

	if (NULL != cd) {
		g_free (cd);
	}

	if (NULL != format) {
		g_free (format);
	}

	if (NULL != location) {
		g_free (location);
	}

	if (NULL != error) {
		g_error_free (error);
	}
	
	if (NULL != cd->stream) {
		g_input_stream_close (cd->stream, NULL, NULL);
	}
	
	if (NULL != stream) {
		g_input_stream_close (cd->stream, NULL, NULL);
	}

	return;
}
Exemple #2
0
static void
do_transcode (DAAPRecord *record, gchar *cachepath, gchar* target_mimetype)
{
	gssize read_size;
	gchar buf[BUFSIZ];
	GError *error = NULL;
	GInputStream *stream = NULL;
	GInputStream *decoded_stream = NULL;
	
	stream = daap_record_read (record, &error);
	if (NULL == stream) {
		gchar *location = NULL;
		g_object_get (record, "location", &location, NULL);
		g_assert (NULL != location);
		g_warning ("Error opening %s: %s", location, error->message);
		g_error_free (error);
		g_free (location);
		goto _return;
	}
	decoded_stream = dmap_gst_input_stream_new (target_mimetype, stream);
	if (NULL == decoded_stream) {
		gchar *location;
		g_object_get (record, "location", &location, NULL);
		g_assert (NULL != location);
		g_warning ("Error opening %s", location);
		g_free (location);
		goto _return;
	}

	FILE *outfile = fopen (cachepath, "w");
	if (outfile == NULL) {
		 g_warning ("Error opening: %s", cachepath);
		 goto _return;
	}

	/* FIXME: is there a glib function to do this? */
	do {
		read_size = g_input_stream_read (decoded_stream,
						 buf,
						 BUFSIZ,
						 NULL,
						&error);
		if (read_size > 0) {
			if (fwrite (buf, 1, read_size, outfile) != read_size) {
				 g_warning ("Error writing transcoded data");
				 goto _return;
			}
		} else if (error != NULL) {
			g_warning ("Error transcoding: %s", error->message);
			g_error_free (error);
			goto _return;
		}
	} while (read_size > 0);

_return:
	if (NULL != outfile) {
		fclose (outfile);
	}

	if (NULL != decoded_stream) {
		g_input_stream_close (decoded_stream, NULL, NULL);
	}

	if (NULL != stream) {
		g_input_stream_close (stream, NULL, NULL); /* FIXME: should this be done in GGstMp3InputStream class? */
	}

	return;
}