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; }
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; }