Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/azatoth/minidlna.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiero <hiero@users.sourceforge.net>2011-03-23 22:24:54 +0300
committerCarl Fürstenberg <azatoth@gmail.com>2011-03-29 03:00:36 +0400
commit68e5b7cc159d36a291d6b9d66b71b56a197a750c (patch)
tree856b4318638923399601e45001eb51fdfeffd3a0
parenteb024683b54aef0fd711a8edda80262ef01dc6b6 (diff)
support transcode Video and Audiopatch-hiero_transcode
This patch supports transcoding for both Video and Audio using mencoder/ffmpeg as transcoder. This uses multiple <res> elements. It means minidlna present multiple media format for one stream. For example, in case source stream is H.264/AAC, minidlna presents both H.264/AAC and MPEG2/AC3. If a client (TV) supports only MPEG2/AC3, it select MPEG2/AC3 stream. All the Video stream can be transcoded to MPEG2/AC3 (DVD) stream. All the Audio stream can be transcoded to LPCM stream. mencoder/ffmpeg is required to be installed. I tested on Ubunts 10.04 and Fedora 12. I hope this patch is helpful to the users who's TV only supports MPEG2 and LPCM. This patch is for minidlna_1.0.18_src.tar.gz. This patch also support Toshiba TV.
-rw-r--r--minidlna.c50
-rw-r--r--minidlna.conf25
-rw-r--r--minidlnatypes.h1
-rw-r--r--options.c8
-rw-r--r--options.h8
-rw-r--r--upnpglobalvars.h23
-rw-r--r--upnphttp.c374
-rw-r--r--upnpsoap.c111
8 files changed, 597 insertions, 3 deletions
diff --git a/minidlna.c b/minidlna.c
index 8fe7885..7c578f4 100644
--- a/minidlna.c
+++ b/minidlna.c
@@ -352,6 +352,12 @@ init(int argc, char * * argv)
char * path;
char real_path[PATH_MAX];
char ext_ip_addr[INET_ADDRSTRLEN] = {'\0'};
+#if 1 // support TRANSCODE
+ int transcode_video_mencoder_options_len = 0;
+ int transcode_video_ffmpeg_options_len = 0;
+ int transcode_audio_ffmpeg_options_len = 0;
+ int len;
+#endif // support TRANSCODE
/* first check if "-f" option is used */
for(i=2; i<argc; i++)
@@ -547,11 +553,55 @@ init(int argc, char * * argv)
if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) )
SETFLAG(DLNA_STRICT_MASK);
break;
+#if 1 // support TRANSCODE
+ case TRANSCODE_A_ENABLE:
+ if(strcmp(ary_options[i].value, "PCM") == 0)
+ transcode_audio = TRANSCODE_AUDIO_PCM;
+ else if(strcmp(ary_options[i].value, "DISABLE") == 0)
+ transcode_audio = TRANSCODE_AUDIO_DISABLE;
+ break;
+ case TRANSCODE_V_TRANSCODER:
+ if(strcmp(ary_options[i].value, "MENCODER") == 0)
+ transcode_video = TRANSCODE_VIDEO_MENCODER;
+ else if(strcmp(ary_options[i].value, "FFMPEG") == 0)
+ transcode_video = TRANSCODE_VIDEO_FFMPEG;
+ else if(strcmp(ary_options[i].value, "DISABLE") == 0)
+ transcode_video = TRANSCODE_VIDEO_DISABLE;
+ break;
+ case TRANSCODE_V_MENCODER_OPTIONS:
+ len = strlen(ary_options[i].value) + 1;
+ len = (transcode_video_mencoder_options_len + len) < TRANSCODE_MENCODER_VIDEO_OPTIONS_MAX_LEN ?
+ len : TRANSCODE_MENCODER_VIDEO_OPTIONS_MAX_LEN - transcode_video_mencoder_options_len - 2;
+ transcode_video_mencoder_options_len +=
+ sprintf(&transcode_video_mencoder_options[transcode_video_mencoder_options_len], "%s ", ary_options[i].value);
+ break;
+ case TRANSCODE_V_FFMPEG_OPTIONS:
+ len = strlen(ary_options[i].value) + 1;
+ len = (transcode_video_ffmpeg_options_len + len) < TRANSCODE_FFMPEG_VIDEO_OPTIONS_MAX_LEN ?
+ len : TRANSCODE_FFMPEG_VIDEO_OPTIONS_MAX_LEN - transcode_video_ffmpeg_options_len - 2;
+ transcode_video_ffmpeg_options_len +=
+ sprintf(&transcode_video_ffmpeg_options[transcode_video_ffmpeg_options_len], "%s ", ary_options[i].value);
+ break;
+ case TRANSCODE_A_FFMPEG_OPTIONS:
+ len = strlen(ary_options[i].value) + 1;
+ len = (transcode_audio_ffmpeg_options_len + len) < TRANSCODE_FFMPEG_AUDIO_OPTIONS_MAX_LEN ?
+ len : TRANSCODE_FFMPEG_AUDIO_OPTIONS_MAX_LEN - transcode_audio_ffmpeg_options_len - 2;
+ transcode_audio_ffmpeg_options_len +=
+ sprintf(&transcode_audio_ffmpeg_options[transcode_audio_ffmpeg_options_len], "%s ", ary_options[i].value);
+ break;
+#endif // support TRANSCODE
default:
fprintf(stderr, "Unknown option in file %s\n",
optionsfile);
}
}
+#if 0 // support TRANSCODE
+ fprintf(stderr, "!!!! transcode_audio : %d\n", transcode_audio);
+ fprintf(stderr, "!!!! transcode_video : %d\n", transcode_video);
+ fprintf(stderr, "!!!! transcode_video_mencoder_options : %s\n", transcode_video_mencoder_options);
+ fprintf(stderr, "!!!! transcode_video_ffmpeg_options : %s\n", transcode_video_ffmpeg_options);
+ fprintf(stderr, "!!!! transcode_audio_ffmpeg_options : %s\n", transcode_audio_ffmpeg_options);
+#endif // support TRANSCODE
}
if( log_path[0] == '\0' )
{
diff --git a/minidlna.conf b/minidlna.conf
index f44c350..be55c67 100644
--- a/minidlna.conf
+++ b/minidlna.conf
@@ -48,3 +48,28 @@ notify_interval=900
# in its XML description
serial=12345678
model_number=1
+
+
+#transcode_audio_enable : =LPCM/DISABLE
+#transcode_video_transcoder : MENCODER/FFMPEG/DISABLE
+#transcode_video_mencoder_options : options for video transcode using mencoder
+# can be divided into multiple "transcode_video_mencoder_options"
+# max characters for every line is 199
+# max characters for total options is 1023
+#transcode_video_ffmpeg_options : options for video transcode using ffmpeg
+# restriction is same as "transcode_video_mencoder_options"
+#transcode_audio_ffmpeg_options : options for audio transcode using ffmpeg
+# restriction is same as "transcode_video_mencoder_options"
+#follows are defaults
+
+transcode_audio_enable=LPCM
+transcode_video_transcoder=MENCODER
+
+#transcode_video_mencoder_options=-msglevel all=-1 -really-quiet -oac lavc
+#transcode_video_mencoder_options=-of mpeg -mpegopts format=mpeg2:muxrate=8000:vbuf_size=1194:abuf_size=64 -vf scale=720:480,harddup -ovc lavc -channels 2
+#transcode_video_mencoder_options=-lavdopts debug=0 -lavcopts autoaspect=1:vcodec=mpeg2video:vbitrate=8000:acodec=ac3:abitrate=128:keyint=18:vqscale=1:vqmin=2
+#transcode_video_mencoder_options=-subdelay 20000 -ofps 30000/1001 -mc 0 -noskip -af lavcresample=48000 -srate 48000
+
+#transcode_video_ffmpeg_options=-v 0 -loglevel quiet -target ntsc-dvd
+
+#transcode_audio_ffmpeg_options=-v 0 -acodec pcm_s16be -f s16be -ar 44100
diff --git a/minidlnatypes.h b/minidlnatypes.h
index 3dbf6e1..a749f43 100644
--- a/minidlnatypes.h
+++ b/minidlnatypes.h
@@ -70,6 +70,7 @@ enum client_types {
EMediaRoom,
ESonyBDP,
ESonyBravia,
+ EToshibaTV,
EStandardDLNA150 = 100
};
diff --git a/options.c b/options.c
index cfca9ee..5b8ec36 100644
--- a/options.c
+++ b/options.c
@@ -60,6 +60,14 @@ static const struct {
{ UPNPLOGDIR, "log_dir" },
{ ENABLE_TIVO, "enable_tivo" },
{ ENABLE_DLNA_STRICT, "strict_dlna" }
+#if 1 // support TRANSCODE
+ ,
+ { TRANSCODE_A_ENABLE, "transcode_audio_enable" },
+ { TRANSCODE_V_TRANSCODER, "transcode_video_transcoder" },
+ { TRANSCODE_V_MENCODER_OPTIONS, "transcode_video_mencoder_options" },
+ { TRANSCODE_V_FFMPEG_OPTIONS, "transcode_video_ffmpeg_options" },
+ { TRANSCODE_A_FFMPEG_OPTIONS, "transcode_audio_ffmpeg_options" }
+#endif // support TRANSCODE
};
int
diff --git a/options.h b/options.h
index a02d7d4..0f3be48 100644
--- a/options.h
+++ b/options.h
@@ -55,6 +55,14 @@ enum upnpconfigoptions {
UPNPLOGDIR, /* base directory to store the log file */
ENABLE_TIVO, /* enable support for streaming images and music to TiVo */
ENABLE_DLNA_STRICT /* strictly adhere to DLNA specs */
+#if 1 // support TRANSCODE
+ ,
+ TRANSCODE_A_ENABLE, /* audio transcode PCM / DISABLE */
+ TRANSCODE_V_TRANSCODER, /* video transcoder MENCODER / FFMPEG / DISABLE */
+ TRANSCODE_V_MENCODER_OPTIONS, /* video mencoder options */
+ TRANSCODE_V_FFMPEG_OPTIONS, /* video ffmpeg options */
+ TRANSCODE_A_FFMPEG_OPTIONS /* audio ffmpeg options */
+#endif // support TRANSCODE
};
/* readoptionsfile()
diff --git a/upnpglobalvars.h b/upnpglobalvars.h
index fbb9dc3..97fb4d0 100644
--- a/upnpglobalvars.h
+++ b/upnpglobalvars.h
@@ -206,4 +206,27 @@ extern short int scanning;
extern volatile short int quitting;
extern volatile uint32_t updateID;
+#if 1 // support TRANSCODE
+/* transcode */
+#define TRANSCODE_MENCODER_VIDEO_OPTIONS_MAX_LEN (1024)
+#define TRANSCODE_FFMPEG_VIDEO_OPTIONS_MAX_LEN (1024)
+#define TRANSCODE_FFMPEG_AUDIO_OPTIONS_MAX_LEN (1024)
+
+enum transcode_audio_enable {
+ TRANSCODE_AUDIO_PCM = 0,
+ TRANSCODE_AUDIO_DISABLE
+};
+enum transcode_video_transcoder {
+ TRANSCODE_VIDEO_MENCODER = 0,
+ TRANSCODE_VIDEO_FFMPEG,
+ TRANSCODE_VIDEO_DISABLE
+};
+
+extern enum transcode_audio_enable transcode_audio;
+extern enum transcode_video_transcoder transcode_video;
+extern char transcode_video_mencoder_options[];
+extern char transcode_video_ffmpeg_options[];
+extern char transcode_audio_ffmpeg_options[];
+#endif // support TRANSCODE
+
#endif
diff --git a/upnphttp.c b/upnphttp.c
index 3017a19..48ca8e9 100644
--- a/upnphttp.c
+++ b/upnphttp.c
@@ -85,6 +85,11 @@
#define MAX_BUFFER_SIZE 2147483647 // 2GB -- Too much?
#define MIN_BUFFER_SIZE 65536
+#if 1 // support TRANSCODE
+#define MAX_BUFFER_SIZE_TRANSCODE 1048576 // 1MB
+#include <signal.h>
+#include <sys/wait.h>
+#endif // support TRANSCODE
#include "icons.c"
struct upnphttp *
@@ -314,6 +319,13 @@ intervening space) by either an integer or the keyword "infinite". */
h->req_client = EMediaRoom;
h->reqflags |= FLAG_MS_PFS;
}
+#if 1 // support ToshibaTV (REGZA)
+ else if(strstrc(p, "UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2", '\r'))
+ {
+ h->req_client = EToshibaTV;
+ h->reqflags |= FLAG_DLNA;
+ }
+#endif // support ToshibaTV (REGZA)
else if(strstrc(p, "DLNADOC/1.50", '\r'))
{
h->req_client = EStandardDLNA150;
@@ -371,10 +383,45 @@ intervening space) by either an integer or the keyword "infinite". */
if( (*p != '1') || !isspace(p[1]) )
h->reqflags |= FLAG_INVALID_REQ;
}
+#if 0
+ else if(strncasecmp(line, "TimeSeekRange.dlna.org", 22)==0)
+ {
+ h->reqflags |= FLAG_TIMESEEK;
+ }
+#else // support TRANSCODE
else if(strncasecmp(line, "TimeSeekRange.dlna.org", 22)==0)
{
+ int hr=0, m=0, s=0, ss=0;
h->reqflags |= FLAG_TIMESEEK;
+ h->req_RangeStart = 0;
+ h->req_RangeEnd = 0;
+ p = colon + 1;
+ while(isspace(*p))
+ p++;
+ if(strncasecmp(p, "npt=", 4)==0) {
+ char buf[32];
+ float tmpf;
+ h->reqflags |= FLAG_RANGE;
+ strncpy(buf, p+4, index(p+4, '-')-(p+4));
+ buf[index(p+4, '-')-(p+4)]='\0';
+ if (index(buf, ':') == NULL) { /* npt format */
+ tmpf = strtof(buf, NULL);
+ h->req_RangeStart = (off_t)(tmpf*1000);
+ tmpf = strtof(index(p+4, '-')+1, NULL);
+ h->req_RangeEnd = (off_t)(tmpf*1000);
+ } else {
+ sscanf(p+4, "%d:%d:%d.%d", &hr, &m, &s, &ss);
+ h->req_RangeStart = hr*3600 + m*60 + s;
+ sscanf(index(p+4, '-')+1, "%d:%d:%d.%d", &hr, &m, &s, &ss);
+ h->req_RangeEnd = (hr*3600 + m*60 + s)*1000 + ss;
+ //h->req_RangeEnd = atoll(index(p+6, '-')+1);
+ //h->req_RangeStart = atoll(p+6);
+ }
+ DPRINTF(E_DEBUG, L_HTTP, "TimeSeekRange Start-End: %lld.%lld - %lld\n",
+ h->req_RangeStart/1000, h->req_RangeStart%1000, h->req_RangeEnd?h->req_RangeEnd/1000:-1, h->req_RangeEnd?h->req_RangeEnd%1000:0);
+ }
}
+#endif // support TRANSCODE
else if(strncasecmp(line, "PlaySpeed.dlna.org", 18)==0)
{
h->reqflags |= FLAG_PLAYSPEED;
@@ -456,7 +503,7 @@ next_header:
break;
}
}
- else if( (n < EStandardDLNA150) && (h->req_client == EStandardDLNA150) )
+ else if( (n < EStandardDLNA150) && ( (h->req_client == EStandardDLNA150) || (h->req_client == EToshibaTV) ) )
{
/* If we know the client and our new detection is generic, use our cached info */
h->reqflags |= clients[n].flags;
@@ -777,8 +824,12 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
return;
}
#if 1 /* 7.3.33.4 */
+#if 0
else if( ((h->reqflags & FLAG_TIMESEEK) || (h->reqflags & FLAG_PLAYSPEED)) &&
!(h->reqflags & FLAG_RANGE) )
+#else // support TRANSCODE
+ else if( (h->reqflags & FLAG_PLAYSPEED) && !(h->reqflags & FLAG_RANGE) )
+#endif // support TRANSCODE
{
DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n",
h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed");
@@ -1171,6 +1222,174 @@ send_file(struct upnphttp * h, int sendfd, off_t offset, off_t end_offset)
free(buf);
}
+
+#if 1 // support TRANSCODE
+
+
+/* options for transcode specified in .conf file */
+enum transcode_audio_enable transcode_audio = TRANSCODE_AUDIO_PCM;
+enum transcode_video_transcoder transcode_video = TRANSCODE_VIDEO_MENCODER;
+char transcode_video_mencoder_options[TRANSCODE_MENCODER_VIDEO_OPTIONS_MAX_LEN] =
+ "-msglevel all=-1 -really-quiet -oac lavc "
+ "-of mpeg -mpegopts format=mpeg2:muxrate=8000:vbuf_size=1194:abuf_size=64 -vf scale=720:480,harddup -ovc lavc -channels 2 "
+ "-lavdopts debug=0 -lavcopts autoaspect=1:vcodec=mpeg2video:vbitrate=8000:acodec=ac3:abitrate=128:keyint=18:vqscale=1:vqmin=2 "
+ "-subdelay 20000 -ofps 30000/1001 -mc 0 -noskip -af lavcresample=48000 -srate 48000";
+char transcode_video_ffmpeg_options[TRANSCODE_FFMPEG_VIDEO_OPTIONS_MAX_LEN] = "-v 0 -target ntsc-dvd";
+char transcode_audio_ffmpeg_options[TRANSCODE_FFMPEG_AUDIO_OPTIONS_MAX_LEN] = "-v 0 -acodec pcm_s16be -f s16be -ar 44100";
+
+#define R (0)
+#define W (1)
+
+int exec_transcode(char *command, int *fd_r, int transcodeAV, int offset, int end_offset)
+{
+ int pipe_c2p[2];
+ int pid;
+ int ret;
+ char position[12], duration[12];
+ char cmd[PATH_MAX];
+ char *transcoder, *options1, *options2, *options3, *options4, start_duration[50], *durationoption;
+
+#define MENCODER_FFMPEG_DIR
+
+ sprintf(position, "%d.%d", offset/1000, offset%1000);
+ sprintf(duration, "%d.%d", (end_offset - offset + 1)/1000, (end_offset - offset + 1)%1000);
+ //DPRINTF(E_INFO, L_HTTP, "position=%s, duration=%s\n", position, duration);
+
+ if (transcodeAV == 'V') {
+//#if 1 //mencoder for Video
+ if (transcode_video == TRANSCODE_VIDEO_MENCODER) {
+ durationoption = "-endpos";
+ transcoder = MENCODER_FFMPEG_DIR"mencoder";
+ options1 = transcode_video_mencoder_options;
+ options2 = "-o -";
+ options3 = "";
+ options4 = "";
+ }
+//#else //FFMpeg for Video
+ else if (transcode_video == TRANSCODE_VIDEO_FFMPEG) {
+ durationoption = "-t";
+ transcoder = MENCODER_FFMPEG_DIR"ffmpeg";
+ options1 = "-i";
+ options2 = "";
+ options3 = transcode_video_ffmpeg_options;
+ options4 = "pipe:1";
+ }
+ else
+ return(-1);
+//#endif
+ } else { /* only FFmpeg supports Audio only */
+ durationoption = "-t";
+ transcoder = MENCODER_FFMPEG_DIR"ffmpeg";
+ options1 = "-i";
+ options2 = "";
+ options3 = transcode_audio_ffmpeg_options;
+ options4 = "pipe:1";
+ }
+
+ if (end_offset >= 0)
+ sprintf(start_duration, "-ss %s %s %s", position, durationoption, duration);
+ else
+ sprintf(start_duration, "-ss %s", position);
+ sprintf(cmd, "%s %s %s %s \"%s\" %s %s", transcoder, start_duration, options1, options2, command, options3, options4);
+ DPRINTF(E_INFO, L_HTTP, "exec %s as following\n%s\n", transcoder, cmd);
+
+ /* Create a pipe. */
+ if(pipe(pipe_c2p)<0) {
+ perror("exec_transcode");
+ return(-1);
+ }
+
+ /* Invoke processs */
+ if((pid=fork())<0){
+ perror("exec_transcode");
+ close(pipe_c2p[R]);
+ close(pipe_c2p[W]);
+ return(-1);
+ }
+ if(pid==0) { /* I'm child */
+ close(pipe_c2p[R]);
+ dup2(pipe_c2p[W],1);
+ close(pipe_c2p[W]);
+ ret = execlp("sh", "sh", "-c", cmd, NULL);
+ if (ret < 0) {
+ perror("popen2");
+ close(pipe_c2p[W]);
+ exit(1);
+ }
+ }
+
+ close(pipe_c2p[W]);
+ *fd_r=pipe_c2p[R];
+
+ return(pid);
+}
+
+
+void
+send_file_transcode(struct upnphttp * h, int sendfd, int offset, int end_offset, char *filename, int transcodeAV)
+{
+ off_t send_size=0, read_stream_size=0, total_byte_read=0, total_byte_send=0;
+ char *buf;
+ int pid, fd_r, pid_status;
+ pid_t ret;
+
+ DPRINTF(E_INFO, L_HTTP, "start transcode and send data\n");
+
+ DPRINTF(E_INFO, L_HTTP, "fork/exec MENCODER/FFMPEG, PPID=%d, PID=%d\n", (int)getppid(), (int)getpid());
+
+ pid = exec_transcode(filename, &fd_r, transcodeAV, offset, end_offset);
+ if (pid<0) return;
+
+ if ((buf = (char *)malloc(MAX_BUFFER_SIZE_TRANSCODE)) == NULL) {
+ DPRINTF(E_ERROR, L_HTTP, "can not malloc in send_file()\n\n");
+ return;
+ }
+
+ total_byte_read=0; total_byte_send=0;
+
+ while(1)
+ {
+ read_stream_size = read(fd_r, buf, MAX_BUFFER_SIZE_TRANSCODE); // read from PIPE
+ if (read_stream_size == 0) {
+ DPRINTF(E_INFO, L_HTTP, "reached to EOF in PID:%d\n", (int)getpid());
+ break; //EOF
+ }
+ total_byte_read += read_stream_size;
+ //DPRINTF(E_INFO, L_HTTP, "received %d bytes from FFMPEG in PID:%d\n", (int)read_stream_size, (int)getpid());
+ send_size = write(h->socket, buf, read_stream_size);
+ if ( send_size != -1 ) total_byte_send += send_size;
+ if ( (send_size != -1) && (send_size != read_stream_size) ) {
+ DPRINTF(E_INFO, L_HTTP, "client is full??\n");
+ read_stream_size -= send_size;
+ usleep(100000); /* wait 100mS */
+ send_size = write(h->socket, buf, read_stream_size);
+ if ( send_size != -1 ) total_byte_send += send_size;
+ }
+ if ( send_size == -1 )
+ {
+ DPRINTF(E_DEBUG, L_HTTP, "sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
+ if( errno != EAGAIN )
+ break;
+ }
+ /*else
+ {
+ DPRINTF(E_DEBUG, L_HTTP, "sent %lld bytes to %d. offset is now %lld.\n", ret, h->socket, offset);
+ }*/
+ }
+ close(fd_r);
+ kill(pid, SIGTERM);
+ ret = waitpid(pid, &pid_status, WNOHANG | WUNTRACED | WCONTINUED);
+ usleep(1000);
+ if (!WIFEXITED(pid_status) || (ret == -1)){
+ kill(pid, SIGKILL);
+ waitpid(pid, &pid_status, WNOHANG | WUNTRACED | WCONTINUED);
+ DPRINTF(E_INFO, L_HTTP, "process PID(%d) was killed\n", (int)pid);
+ }
+ DPRINTF(E_INFO, L_HTTP, "total bytes : read=%lld, send=%lld\n", total_byte_read, total_byte_send);
+ free(buf);
+}
+#endif // support TRANSCODE
+
void
SendResp_icon(struct upnphttp * h, char * icon)
{
@@ -1688,11 +1907,35 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
off_t total, offset, size;
sqlite_int64 id;
int sendfh;
+#if 0
static struct { sqlite_int64 id; char path[PATH_MAX]; char mime[32]; char dlna[64]; } last_file = { 0 };
+#else // support TRANSCODE : add Transcoded Audio
+ static struct { sqlite_int64 id; char path[PATH_MAX]; char mime[32]; char dlna[128]; int duration; off_t size; } last_file = { 0 };
+ int transcodeAV=0;
+ char *transcoded_mime=NULL, *transcoded_dlna=NULL;
+#endif // support TRANSCODE
#if USE_FORK
pid_t newpid = 0;
#endif
+#if 1 // support TRANSCODE
+ transcodeAV = 0;
+ if (strncmp(object, "TranscodeVideo/", 15) == 0 )
+ {
+ transcodeAV = 'V';
+ object += 15;
+ transcoded_mime = "video/mpeg";
+ transcoded_dlna = "DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=10;DLNA.ORG_CI=1";
+ }
+ else if (strncmp(object, "TranscodeAudio/", 15) == 0 )
+ {
+ transcodeAV = 'A';
+ object += 15;
+ transcoded_mime = "audio/L16;rate=44100;channels=2";
+ transcoded_dlna = "DLNA.ORG_PN=LPCM;DLNA.ORG_OP=10;DLNA.ORG_CI=1";
+ }
+#endif // support TRANSCODE
+
id = strtoll(object, NULL, 10);
if( id != last_file.id )
{
@@ -1735,6 +1978,11 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
{
last_file.mime[0] = '\0';
}
+#if 1 // support TRANSCODE
+ if (transcodeAV)
+ strcpy(last_file.dlna, transcoded_dlna);
+ else
+#endif // support TRANSCODE
if( result[5] )
snprintf(last_file.dlna, sizeof(last_file.dlna), "DLNA.ORG_PN=%s", result[5]);
else if( h->reqflags & FLAG_DLNA )
@@ -1742,6 +1990,39 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
else
last_file.dlna[0] = '\0';
sqlite3_free_table(result);
+
+#if 1 // support TRANSCODE
+ /* get more columns */
+ sprintf(sql_buf, "SELECT DURATION, SIZE from DETAILS where ID = '%lld'", id);
+ ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
+ if( (ret != SQLITE_OK) )
+ {
+ DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", id);
+ Send500(h);
+ return;
+ }
+ if( !rows )
+ {
+ DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
+ sqlite3_free_table(result);
+ Send404(h);
+ return;
+ }
+ if( result[2] )
+ {
+ int h, m, s, ss;
+ sscanf(result[2], "%d:%d:%d.%d", &h, &m, &s, &ss);
+ last_file.duration = (3600*h + 60*m + s)*1000 + ss;
+ //printf("!!! duration= %d:%02d:%02d.%03d = %d\n",h, m, s, ss, last_file.duration);
+ }
+ if( result[3] )
+ {
+ last_file.size = (off_t)atoll(result[3]);
+ //printf("!!! %lld\n", last_file.size);
+ }
+ sqlite3_free_table(result);
+#endif // support TRANSCODE
+
}
#if USE_FORK
newpid = fork();
@@ -1790,13 +2071,27 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
}
size = lseek(sendfh, 0, SEEK_END);
lseek(sendfh, 0, SEEK_SET);
-
+#if 0
sprintf(header, "HTTP/1.1 20%c OK\r\n"
"Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'), last_file.mime);
+#else // support TRANSCODE
+ sprintf(header, "HTTP/1.1 20%c OK\r\n"
+ "Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'),
+ transcodeAV ? transcoded_mime : last_file.mime);
+#endif // support TRANSCODE
if( h->reqflags & FLAG_RANGE )
{
if( !h->req_RangeEnd )
+#if 0
h->req_RangeEnd = size;
+#else // support TRANSCODE
+ {
+ if (!(h->reqflags & FLAG_TIMESEEK))
+ h->req_RangeEnd = size;
+ else
+ h->req_RangeEnd = last_file.duration;
+ }
+#endif // support TRANSCODE
if( (h->req_RangeStart > h->req_RangeEnd) || (h->req_RangeStart < 0) )
{
DPRINTF(E_WARN, L_HTTP, "Specified range was invalid!\n");
@@ -1804,14 +2099,19 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
close(sendfh);
goto error;
}
+#if 0
if( h->req_RangeEnd > size )
+#else // support TRANSCODE
+ if ( (!(h->reqflags & FLAG_TIMESEEK) && ( h->req_RangeEnd > size ) )
+ || ( (h->reqflags & FLAG_TIMESEEK) && ( h->req_RangeEnd > last_file.duration ) ) )
+#endif // support TRANSCODE
{
DPRINTF(E_WARN, L_HTTP, "Specified range was outside file boundaries!\n");
Send416(h);
close(sendfh);
goto error;
}
-
+#if 0
if( h->req_RangeEnd < size )
{
total = h->req_RangeEnd - h->req_RangeStart + 1;
@@ -1827,7 +2127,47 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
"Content-Range: bytes %jd-%jd/%jd\r\n",
total, h->req_RangeStart, size-1, size);
}
+#else // support TRANSCODE
+ if ( !(h->reqflags & FLAG_TIMESEEK) )
+ {
+ if( h->req_RangeEnd < size )
+ {
+ total = h->req_RangeEnd - h->req_RangeStart + 1;
+ sprintf(hdr_buf, "Content-Length: %jd\r\n"
+ "Content-Range: bytes %jd-%jd/%jd\r\n",
+ total, h->req_RangeStart, h->req_RangeEnd, size);
+ }
+ else
+ {
+ h->req_RangeEnd = size;
+ total = size - h->req_RangeStart;
+ sprintf(hdr_buf, "Content-Length: %jd\r\n"
+ "Content-Range: bytes %jd-%jd/%jd\r\n",
+ total, h->req_RangeStart, size-1, size);
+ }
+ }
+ else {
+ if( h->req_RangeEnd >= last_file.duration ) h->req_RangeEnd = last_file.duration;
+ sprintf(hdr_buf,
+ "TimeSeekRange.dlna.org : npt= %jd.%jd-%jd.%jd/%d.%d\r\n",
+ h->req_RangeStart/1000, h->req_RangeStart%1000,
+ (h->req_RangeEnd-1)/1000, (h->req_RangeEnd-1)%1000,
+ last_file.duration/1000, last_file.duration%1000);
+ }
+#endif // support TRANSCODE
+ }
+#if 1 // support TRANSCODE
+ else if ( !(h->reqflags & FLAG_TIMESEEK) && !(h->reqflags & FLAG_RANGE) && transcodeAV)
+ {
+ h->req_RangeStart = 0;
+ h->req_RangeEnd = last_file.duration;
+ sprintf(hdr_buf,
+ "TimeSeekRange.dlna.org : npt= %jd.%jd-%jd.%jd/%d.%d\r\n",
+ h->req_RangeStart/1000, h->req_RangeStart%1000,
+ (h->req_RangeEnd-1)/1000, (h->req_RangeEnd-1)%1000,
+ last_file.duration/1000, last_file.duration%1000);
}
+#endif // support TRANSCODE
else
{
h->req_RangeEnd = size;
@@ -1868,6 +2208,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
}
}
+#if 0
sprintf(hdr_buf, "Accept-Ranges: bytes\r\n"
"Connection: close\r\n"
"Date: %s\r\n"
@@ -1876,11 +2217,38 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
"contentFeatures.dlna.org: %s\r\n"
"Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
date, last_file.dlna);
+#else // support TRANSCODE
+ sprintf(hdr_buf, "Accept-Ranges: %s\r\n"
+ "Connection: close\r\n"
+ "Date: %s\r\n"
+ "EXT:\r\n"
+ "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
+ "contentFeatures.dlna.org: %s\r\n"
+ "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
+ transcodeAV ? "none" : "bytes",
+ date, last_file.dlna);
+#endif // support TRANSCODE
strcat(header, hdr_buf);
+#if 1 // support TRANSCODE
+ DPRINTF(E_INFO, L_HTTP, "Following is response\n%s\n", header);
+#endif // support TRANSCODE
+
if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) )
{
+#if 1 // support TRANSCODE
+ //DPRINTF(E_INFO, L_HTTP, "reqflags=%08x\n", h->reqflags);
+ DPRINTF(E_INFO, L_HTTP, "last_file.mime=%s\n", last_file.mime);
+ if (transcodeAV ) {
+ //if (h->req_RangeStart <= 2) h->req_RangeStart = 0;
+ if (h->req_RangeEnd == last_file.duration) h->req_RangeEnd = -1;
+ send_file_transcode(h, sendfh, (int)h->req_RangeStart, (int)h->req_RangeEnd, last_file.path, transcodeAV);
+ } else {
+ send_file(h, sendfh, offset, h->req_RangeEnd);
+ }
+#else
send_file(h, sendfh, offset, h->req_RangeEnd);
+#endif // support TRANSCODE
}
close(sendfh);
diff --git a/upnpsoap.c b/upnpsoap.c
index cef9729..64806da 100644
--- a/upnpsoap.c
+++ b/upnpsoap.c
@@ -883,6 +883,117 @@ callback(void *args, int argc, char **argv, char **azColName)
passed_args->size += ret;
}
}
+#if 1 // add "protocolInfo" for Toshiba TV (REGZA)
+ if( (*mime == 'v') && dlna_pn && ( passed_args->client == EToshibaTV ) ) {
+ if ((strcmp(dlna_pn, "MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0") == 0)
+ || (strcmp(dlna_pn, "MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0") == 0)
+ || (strcmp(dlna_pn, "AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0") == 0)
+ ) {
+ sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
+
+ ret = sprintf(str_buf, "&lt;res ");
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ if( size && (passed_args->filter & FILTER_RES_SIZE) ) {
+ ret = sprintf(str_buf, "size=\"%s\" ", size);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
+ ret = sprintf(str_buf, "duration=\"%s\" ", duration);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
+ ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
+ ret = sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
+ ret = sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
+ ret = sprintf(str_buf, "resolution=\"%s\" ", resolution);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ ret = sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
+ "http://%s:%d/MediaItems/%s.%s"
+ "&lt;/res&gt;",
+ mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID, ext);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ }
+#endif // add "protocolInfo" for Toshiba TV (REGZA)
+#if 1 // support TRANSCODE : add Transcoded Video
+ if( ( strncmp(mime, "video/", 6) == 0 ) && (transcode_video != TRANSCODE_VIDEO_DISABLE) ){
+ ret = sprintf(str_buf, "&lt;res ");
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
+ ret = sprintf(str_buf, "duration=\"%s\" ", duration);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
+ ret = sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+ ret = sprintf(str_buf, "resolution=\"720x480\" ");
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+
+ ret = sprintf(str_buf, "protocolInfo=\"http-get:*:video/mpeg:"
+ "DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=10;DLNA.ORG_CI=1;"
+ "DLNA.ORG_FLAGS=01500000000000000000000000000000\"&gt;"
+ "http://%s:%d/MediaItems/TranscodeVideo/%s.%s"
+ "&lt;/res&gt;",
+ lan_addr[0].str, runtime_vars.port, detailID, ext);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+#endif // support TRANSCODE : add Transcoded Video
+#if 1 // support TRANSCODE : add Transcoded Audio
+ if ( ( strncmp(mime, "audio/", 6) == 0 ) && (transcode_audio != TRANSCODE_AUDIO_DISABLE) ) {
+ ret = sprintf(str_buf, "&lt;res ");
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
+ ret = sprintf(str_buf, "duration=\"%s\" ", duration);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+
+ if( passed_args->client == EXbox )
+ ret = sprintf(str_buf, "bitrate=\"%d\" ", 1411200/1024);
+ else
+ ret = sprintf(str_buf, "bitrate=\"%d\" ", 1411200/8);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+
+ ret = sprintf(str_buf, "sampleFrequency=\"44100\" nrAudioChannels=\"2\" bitsPerSample=\"16\" ");
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+
+ ret = sprintf(str_buf, "protocolInfo=\"http-get:*:audio/L16;rate=44100;channels=2:"
+ "DLNA.ORG_PN=LPCM;DLNA.ORG_OP=10;DLNA.ORG_CI=1;"
+ "DLNA.ORG_FLAGS=01500000000000000000000000000000\"&gt;"
+ "http://%s:%d/MediaItems/TranscodeAudio/%s.%s"
+ "&lt;/res&gt;",
+ lan_addr[0].str, runtime_vars.port, detailID, ext);
+ memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
+ passed_args->size += ret;
+ }
+#endif // support TRANSCODE : add Transcoded Audio
}
ret = sprintf(str_buf, "&lt;/item&gt;");
}