diff --git a/metadata.c b/metadata.c
index 2bfa8b9..dd2fec5 100644
--- a/metadata.c
+++ b/metadata.c
@@ -337,6 +337,16 @@ GetAudioMetadata(const char *path, char *name)
 		strcpy(type, "pcm");
 		m.mime = strdup("audio/L16");
 	}
+	else if( ends_with(path, ".dsf") )
+	{
+		strcpy(type, "dsf");
+		m.mime = strdup("audio/x-dsd");
+	}
+	else if( ends_with(path, ".dff") )
+	{
+		strcpy(type, "dff");
+		m.mime = strdup("audio/x-dsd");
+	}
 	else
 	{
 		DPRINTF(E_WARN, L_METADATA, "Unhandled file extension on %s\n", path);
diff --git a/tagutils/.dirstamp b/tagutils/.dirstamp
new file mode 100644
index 0000000..e69de29
diff --git a/tagutils/tagutils-dff.c b/tagutils/tagutils-dff.c
new file mode 100644
index 0000000..b250838
--- /dev/null
+++ b/tagutils/tagutils-dff.c
@@ -0,0 +1,393 @@
+//=========================================================================
+// FILENAME     : tagutils-dff.c
+// DESCRIPTION  : DFF metadata reader
+//=========================================================================
+// Copyright (c) 2014 Takeshich NAKAMURA
+//=========================================================================
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define GET_DFF_INT64(p) ((((uint64_t)((p)[0])) << 56) |   \
+                          (((uint64_t)((p)[1])) << 48) |   \
+                          (((uint64_t)((p)[2])) << 40) |   \
+                          (((uint64_t)((p)[3])) << 32) |   \
+                          (((uint64_t)((p)[4])) << 24) |   \
+                          (((uint64_t)((p)[5])) << 16) |   \
+                          (((uint64_t)((p)[6])) << 8) |    \
+                          (((uint64_t)((p)[7])) ))
+
+#define GET_DFF_INT32(p) ((((uint32_t)((p)[0])) << 24) |   \
+                          (((uint32_t)((p)[1])) << 16) |   \
+                          (((uint32_t)((p)[2])) << 8) |     \
+                          (((uint32_t)((p)[3])) ))
+
+#define GET_DFF_INT16(p) ((((uint16_t)((p)[0])) << 8) |   \
+                          (((uint16_t)((p)[1])) )) 
+
+static int
+_get_dfffileinfo(char *file, struct song_metadata *psong)
+{
+	FILE *fp;
+	uint32_t len;
+	uint32_t rt;
+	unsigned char hdr[32] = {0};
+
+	uint64_t totalsize = 0;
+	uint64_t propckDataSize = 0;
+	uint64_t count = 0;
+	uint32_t samplerate = 0;
+	uint16_t channels = 0;
+	//DST
+	uint64_t dstickDataSize = 0;
+	uint32_t numFrames = 0;
+	uint16_t frameRate = 0;
+	unsigned char frteckData[18] = {0};
+	unsigned char dstickData[12] = {0};
+	uint64_t totalcount = 0;
+	unsigned char ckbuf[12] = {0};
+	unsigned char compressionType[4] = {0};
+	unsigned char dsdsdckData[12] = {0};
+	uint64_t dsdsdckDataSize = 0;
+	uint64_t cmprckDataSize = 0;
+	uint64_t abssckDataSize = 0;
+	uint64_t lscockDataSize = 0;
+	uint64_t comtckDataSize = 0;
+	uint64_t diinckDataSize = 0;
+	uint64_t diarckDataSize = 0;
+	uint64_t ditickDataSize = 0;
+	uint64_t manfckDataSize = 0;
+
+	//DPRINTF(E_DEBUG,L_SCANNER,"Getting DFF fileinfo =%s\n",file);    
+	
+	if((fp = fopen(file, "rb")) == NULL)
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Could not create file handle\n");
+		return -1;
+	}
+		
+	len = 32;
+	//Form DSD chunk
+	if(!(rt = fread(hdr, len, 1,fp)))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Could not read Form DSD chunk from %s\n", file);
+		fclose(fp);
+ 		return -1;
+	}
+	
+	if(strncmp((char*)hdr, "FRM8", 4))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Invalid Form DSD chunk in %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+        
+	totalsize = GET_DFF_INT64(hdr + 4) ;
+       
+	if(strncmp((char*)hdr+12, "DSD ", 4))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Invalid Form DSD chunk in %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+
+	//FVER chunk
+	if(strncmp((char*)hdr+16, "FVER", 4))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Invalid Format Version Chunk in %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+
+	totalsize -= 16;
+	while(totalcount < totalsize - 4)
+	{
+	
+		if(!(rt = fread(ckbuf, sizeof(ckbuf), 1,fp)))
+		{
+			//DPRINTF(E_WARN, L_SCANNER, "Could not read chunk header from %s\n", file);
+			//fclose(fp);
+ 			//return -1;
+			break;
+		}
+	
+		//Property chunk
+		if(strncmp((char*)ckbuf, "PROP", 4) == 0)
+ 		{
+
+			
+			propckDataSize = GET_DFF_INT64(ckbuf + 4);
+			totalcount += propckDataSize + 12;
+
+			unsigned char propckData[propckDataSize];
+
+			if(!(rt = fread(propckData, propckDataSize, 1,fp)))
+			{
+				DPRINTF(E_WARN, L_SCANNER, "Could not read Property chunk from %s\n", file);
+				fclose(fp);
+				return -1;
+			}
+				
+			if(strncmp((char*)propckData, "SND ", 4))
+ 			{
+ 				DPRINTF(E_WARN, L_SCANNER, "Invalid Property chunk in %s\n", file);
+ 				fclose(fp);
+ 				return -1;
+ 			}
+
+			count += 4;	
+			while(count < propckDataSize)
+			{
+				if(strncmp((char*)propckData+count, "FS  ", 4) == 0)
+				{
+					//Sample Rate Chunk
+					count += 12;
+					samplerate = GET_DFF_INT32(propckData+count);
+					psong->samplerate = samplerate;
+					count += 4;
+					
+					//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Sample Rate is %d\n", psong->samplerate);
+				}else if(strncmp((char*)propckData+count, "CHNL", 4) == 0)
+				{
+					//Channels Chunk
+					count += 12;
+					channels = GET_DFF_INT16(propckData+count);
+					psong->channels = channels;
+					count += channels * 4 + 2;
+					
+					//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "channels is %d\n", channels);
+				}else if(strncmp((char*)propckData+count, "CMPR", 4) == 0)
+				{
+					//Compression Type Chunk
+					count += 4;
+					cmprckDataSize = GET_DFF_INT64(propckData+count);
+					count += 8;
+					strncpy((char*)compressionType,(char*)propckData+count,4);
+					count += cmprckDataSize;
+					
+				}else if(strncmp((char*)propckData+count, "ABSS", 4) == 0)
+				{
+					//Absolute Start Time Chunk
+					count += 4;
+					abssckDataSize = GET_DFF_INT64(propckData+count);
+					count += abssckDataSize + 8;
+					
+				}else if(strncmp((char*)propckData+count, "LSCO", 4) == 0)
+				{
+					//Loudsperaker Configuration Chunk
+					count += 4;
+					lscockDataSize = GET_DFF_INT64(propckData+count);
+					count += lscockDataSize + 8;
+					
+				}else{
+					break;
+				}
+			}
+
+			//bitrate bitpersample is 1bit
+			psong->bitrate = channels * samplerate * 1;
+
+			//DSD/DST Sound Data Chunk
+			len = 12;
+			if(!(rt = fread(dsdsdckData, len, 1,fp)))
+			{
+				DPRINTF(E_WARN, L_SCANNER, "Could not read DSD/DST Sound Data chunk from %s\n", file);
+				fclose(fp);
+				return -1;
+			}
+	
+			if(strncmp((char*)compressionType,(char*)dsdsdckData , 4))
+			{
+				DPRINTF(E_WARN, L_SCANNER, "Invalid DSD/DST Sound Data chunk in %s\n", file);
+				fclose(fp);
+				return -1;
+			}
+
+			if(strncmp((char*)dsdsdckData,"DSD " , 4) == 0)
+			{
+				//DSD
+				dsdsdckDataSize = GET_DFF_INT64(dsdsdckData+4);
+				totalcount += dsdsdckDataSize + 12;
+				psong->song_length = (int)((double)dsdsdckDataSize / (double)samplerate / (double)channels * 8 * 1000);
+				
+				//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "songlength is %d\n", psong->song_length);
+				
+				fseeko(fp, dsdsdckDataSize,SEEK_CUR);
+			}else if(strncmp((char*)dsdsdckData,"DST " , 4) == 0)
+			{
+				//DST
+				dsdsdckDataSize = GET_DFF_INT64(dsdsdckData+4);
+				totalcount += dsdsdckDataSize + 12;
+	
+				//DST Frame Information chunk	
+				if(!(rt = fread(frteckData, 18, 1,fp)))
+				{
+					DPRINTF(E_WARN, L_SCANNER, "Could not read DST Frame Information chunk from %s\n", file);
+					fclose(fp);
+					return -1;
+				}
+
+				if(strncmp((char*)frteckData ,"FRTE", 4) == 0)
+				{
+					//uint64_t frteckDataSize = GET_DFF_INT64(frteckData+4);
+					numFrames = GET_DFF_INT32((char*)frteckData+12);
+					frameRate = GET_DFF_INT16((char*)frteckData+16);
+
+					psong->song_length = numFrames / frameRate * 1000;
+	
+					fseeko(fp, dsdsdckDataSize-18,SEEK_CUR); 
+				}else
+				{
+					DPRINTF(E_WARN, L_SCANNER, "Invalid DST Frame Information chunk in %s\n", file);
+					fclose(fp);
+					return -1;
+				}
+
+				//DST Sound Index Chunk
+				if(!(rt = fread(dstickData, 12, 1,fp)))
+				{
+					if (ferror(fp))
+					{
+						DPRINTF(E_WARN, L_SCANNER, "Could not read DST Sound Index chunk from %s\n", file);
+						fclose(fp);
+						return -1;
+					}else
+					{
+						//EOF
+						break;
+					}
+				}
+
+				if(strncmp((char*)dstickData ,"DSTI", 4) == 0)
+				{
+					dstickDataSize = GET_DFF_INT64(dstickData+4);
+					totalcount += dstickDataSize + 12;
+					fseeko(fp, dstickDataSize,SEEK_CUR);
+				}else
+				{
+					fseeko(fp, -12,SEEK_CUR);
+				}
+			}else
+			{
+				DPRINTF(E_WARN, L_SCANNER, "Invalid DSD/DST Sound Data chunk in %s\n", file);
+				fclose(fp);
+				return -1;
+			}
+		}else if(!strncmp((char*)ckbuf ,"COMT", 4))
+		{
+			//COMT Chunk
+			comtckDataSize = GET_DFF_INT64(ckbuf+4);
+			totalcount += comtckDataSize + 12;
+			fseeko(fp, comtckDataSize,SEEK_CUR);
+
+		}else if(!strncmp((char*)ckbuf ,"DIIN", 4))
+		{
+			//Edited Master Information chunk
+			diinckDataSize = GET_DFF_INT64(ckbuf+4);
+			unsigned char diinckData[diinckDataSize];
+			totalcount += diinckDataSize + 12;
+			
+			if(!(rt = fread(diinckData, diinckDataSize, 1,fp)))
+			{
+				DPRINTF(E_WARN, L_SCANNER, "Could not read Edited Master Information chunk from %s\n", file);
+				fclose(fp);
+				return -1;
+			}
+
+			uint64_t icount = 0;
+			while(icount < diinckDataSize)
+			{
+				if(!strncmp((char*)diinckData+icount ,"EMID", 4))
+				{
+					//Edited Master ID chunk
+					icount += 4;	
+					icount += GET_DFF_INT64(diinckData+icount) + 8;
+
+				}else if(!strncmp((char*)diinckData+icount ,"MARK", 4))
+				{
+					//Master Chunk
+					icount += 4;	
+					icount += GET_DFF_INT64(diinckData+icount) + 8;
+
+				}else if(!strncmp((char*)diinckData+icount ,"DIAR", 4))
+				{
+					//Artist Chunk
+					icount += 4;
+					diarckDataSize = GET_DFF_INT64(diinckData+icount);
+					unsigned char arttext[diarckDataSize +1 - 4];
+					
+					icount += 12;	
+					
+					memset(arttext,0x00,sizeof(arttext));	
+					strncpy((char*)arttext,(char*)diinckData+icount,sizeof(arttext)-1);
+					psong->contributor[ROLE_ARTIST] = strdup((char*)&arttext[0]);
+
+					icount += diarckDataSize - 4;
+					
+				}else if(!strncmp((char*)diinckData+icount ,"DITI", 4))
+				{
+					//Title Chunk
+					icount += 4;
+					ditickDataSize = GET_DFF_INT64(diinckData+icount);
+					unsigned char titletext[ditickDataSize+1 - 4];
+					
+					icount += 12;
+					
+					memset(titletext,0x00,sizeof(titletext));	
+					strncpy((char*)titletext,(char*)diinckData+icount,sizeof(titletext)-1);
+					psong->title =  strdup((char*)&titletext[0]);
+					icount += ditickDataSize - 4;
+					
+				}else
+				{
+					break;
+				}
+			}
+		}else if(!strncmp((char*)ckbuf ,"MANF", 4))
+		{
+			//Manufacturer Specific Chunk
+			manfckDataSize = GET_DFF_INT64(ckbuf+4);
+			totalcount += manfckDataSize + 12;
+			fseeko(fp, manfckDataSize,SEEK_CUR);
+
+		}
+	}
+	
+	fclose(fp);
+
+	//DPRINTF(E_DEBUG, L_SCANNER, "totalsize is 0x%016lx\n", (long unsigned int)totalsize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "propckDataSize is 0x%016lx\n", (long unsigned int)propckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "cmprckDataSize is 0x%016lx\n", (long unsigned int)cmprckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "abssckDataSize is 0x%016lx\n", (long unsigned int)abssckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "lscockDataSize is 0x%016lx\n", (long unsigned int)lscockDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "dsdsdckDataSize is 0x%016lx\n", (long unsigned int)dsdsdckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "dstickDataSize is 0x%016lx\n", (long unsigned int)dstickDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "comtckDataSize is 0x%016lx\n", (long unsigned int)comtckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "diinckDataSize is 0x%016lx\n", (long unsigned int)diinckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "diarckDataSize is 0x%016lx\n", (long unsigned int)diarckDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "ditickDataSize is 0x%016lx\n", (long unsigned int)ditickDataSize);
+	//DPRINTF(E_DEBUG, L_SCANNER, "manfckDataSize is 0x%016lx\n", (long unsigned int)manfckDataSize);
+	
+
+	//DPRINTF(E_DEBUG, L_SCANNER, "Got dff fileinfo successfully=%s\n", file);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "TITLE is %s\n",psong->title );
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "ARTIST is %s\n",psong->contributor[ROLE_ARTIST]);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "samplerate is  %d\n", psong->samplerate);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "song_length is  %d\n", psong->song_length);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "channels are  %d\n", psong->channels);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "bitrate is  %d\n", psong->bitrate);
+
+    xasprintf(&(psong->dlna_pn), "DFF");
+    return 0;
+}
diff --git a/tagutils/tagutils-dff.h b/tagutils/tagutils-dff.h
new file mode 100644
index 0000000..7f3e3a4
--- /dev/null
+++ b/tagutils/tagutils-dff.h
@@ -0,0 +1,22 @@
+//=========================================================================
+// FILENAME     : tagutils-dff.h
+// DESCRIPTION  : DFF metadata reader
+//=========================================================================
+// Copyright (c) 2014 Takeshich NAKAMURA
+//=========================================================================
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+static int _get_dfffileinfo(char *file, struct song_metadata *psong);
diff --git a/tagutils/tagutils-dsf.c b/tagutils/tagutils-dsf.c
new file mode 100644
index 0000000..49d1132
--- /dev/null
+++ b/tagutils/tagutils-dsf.c
@@ -0,0 +1,428 @@
+//=========================================================================
+// FILENAME	: tagutils-dsf.c
+// DESCRIPTION	: DSF metadata reader
+//=========================================================================
+// Copyright (c) 2014 Takeshich NAKAMURA 
+// based on tagutils-mp3.c
+//=========================================================================
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define GET_DSF_INT64(p) ((((uint64_t)((p)[7])) << 56) |   \
+			  (((uint64_t)((p)[6])) << 48) |   \
+			  (((uint64_t)((p)[5])) << 40) |   \
+			  (((uint64_t)((p)[4])) << 32) |   \
+		  	  (((uint64_t)((p)[3])) << 24) |   \
+			  (((uint64_t)((p)[2])) << 16) |   \
+			  (((uint64_t)((p)[1])) << 8) |	   \
+			  (((uint64_t)((p)[0]))))
+
+#define GET_DSF_INT32(p) ((((uint8_t)((p)[3])) << 24) |   \
+			  (((uint8_t)((p)[2])) << 16) |   \
+			  (((uint8_t)((p)[1])) << 8) |	   \
+			  (((uint8_t)((p)[0]))))
+			
+static int
+_get_dsftags(char *file, struct song_metadata *psong)
+{
+	struct id3_tag *pid3tag;
+	struct id3_frame *pid3frame;
+	int err;
+	int index;
+	int used;
+	unsigned char *utf8_text;
+	int genre = WINAMP_GENRE_UNKNOWN;
+	int have_utf8;
+	int have_text;
+	id3_ucs4_t const *native_text;
+	char *tmp;
+	int got_numeric_genre;
+	id3_byte_t const *image;
+	id3_length_t image_size = 0;
+	
+	FILE *fp;
+	struct id3header *pid3;
+	uint32_t len;
+	unsigned char hdr[28] = {0};
+	uint64_t total_size = 0;
+	uint64_t pointer_to_metadata_chunk = 0;
+	uint64_t metadata_chunk_size = 0;
+	unsigned char *id3tagbuf = NULL;
+
+	//DEBUG DPRINTF(E_DEBUG,L_SCANNER,"Getting DSF file info\n");
+
+	if((fp = fopen(file, "rb")) == NULL)
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Could not create file handle\n");
+		return -1;
+	}
+
+	len = 28;
+	if(!(len = fread(hdr, len, 1,fp)))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Could not read DSD Chunk from %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+
+	if(strncmp((char*)hdr, "DSD ", 4))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Invalid DSD Chunk header in %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+
+	total_size = GET_DSF_INT64(hdr + 12);
+	pointer_to_metadata_chunk = GET_DSF_INT64(hdr + 20);
+	metadata_chunk_size = total_size - pointer_to_metadata_chunk;
+	
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%llu\n", total_size);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%llu\n", pointer_to_metadata_chunk);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "%llu\n", metadata_chunk_size);
+
+	//check invalid metadata
+	if(total_size == 0)
+	{
+		fclose(fp);
+		DPRINTF(E_INFO, L_SCANNER, "Invalid TotalDataSize in %s\n", file);
+		return 0;
+	}
+
+	if(pointer_to_metadata_chunk == 0)
+	{
+		fclose(fp);
+		DPRINTF(E_INFO, L_SCANNER, "Metadata doesn't exist %s\n", file);
+		return 0;
+	}
+
+	if(total_size > pointer_to_metadata_chunk)
+	{
+		metadata_chunk_size = total_size - pointer_to_metadata_chunk;
+	}
+	else
+	{
+		fclose(fp);
+		DPRINTF(E_INFO, L_SCANNER, "Invalid PointerToMetadata in %s\n", file);
+		return 0;
+	}
+
+	fseeko(fp, pointer_to_metadata_chunk,SEEK_SET);
+
+	id3tagbuf = (unsigned char *)malloc(sizeof(unsigned char)*metadata_chunk_size);
+	if(id3tagbuf == NULL)
+	{
+		fclose(fp);
+		DPRINTF(E_WARN, L_SCANNER, "Out of memory.Big MetadataSize in %s\n",file);
+		return -1;
+	}
+	memset(id3tagbuf, 0,sizeof(unsigned char)*metadata_chunk_size);
+	
+	if(!(len = fread(id3tagbuf,metadata_chunk_size,1,fp)))
+	{
+		fclose(fp);
+		free(id3tagbuf);
+		DPRINTF(E_WARN, L_SCANNER, "Could not read Metadata Chunk from %s\n", file);
+		return -1;
+	}
+	
+	pid3tag = id3_tag_parse(id3tagbuf,metadata_chunk_size);
+	
+	if(!pid3tag)
+	{
+		free(id3tagbuf);
+		err = errno;
+		fclose(fp);
+		errno = err;
+		DPRINTF(E_WARN, L_SCANNER, "Cannot get ID3 tag for %s\n", file);
+		return -1;
+	}
+
+	pid3 = (struct id3header*)id3tagbuf;
+
+	if(strncmp((char*)pid3->id, "ID3", 3) == 0)
+	{
+		char tagversion[16];
+
+		/* found an ID3 header... */
+		snprintf(tagversion, sizeof(tagversion), "ID3v2.%d.%d",
+			 pid3->version[0], pid3->version[1]);
+		psong->tagversion = strdup(tagversion);
+	}
+	pid3 = NULL;
+	
+	index = 0;
+	while((pid3frame = id3_tag_findframe(pid3tag, "", index)))
+	{
+		used = 0;
+		utf8_text = NULL;
+		native_text = NULL;
+		have_utf8 = 0;
+		have_text = 0;
+
+		if(!strcmp(pid3frame->id, "YTCP"))   /* for id3v2.2 */
+		{
+			psong->compilation = 1;
+			DPRINTF(E_DEBUG, L_SCANNER, "Compilation: %d [%s]\n", psong->compilation, basename(file));
+		}
+		else if(!strcmp(pid3frame->id, "APIC") && !image_size)
+		{
+			if( (strcmp((char*)id3_field_getlatin1(&pid3frame->fields[1]), "image/jpeg") == 0) ||
+			    (strcmp((char*)id3_field_getlatin1(&pid3frame->fields[1]), "image/jpg") == 0) ||
+			    (strcmp((char*)id3_field_getlatin1(&pid3frame->fields[1]), "jpeg") == 0) )
+			{
+				image = id3_field_getbinarydata(&pid3frame->fields[4], &image_size);
+				if( image_size )
+				{
+					psong->image = malloc(image_size);
+					memcpy(psong->image, image, image_size);
+					psong->image_size = image_size;
+					//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Found thumbnail: %d\n", psong->image_size);
+				}
+			}
+		}
+
+		if(((pid3frame->id[0] == 'T') || (strcmp(pid3frame->id, "COMM") == 0)) &&
+		   (id3_field_getnstrings(&pid3frame->fields[1])))
+			have_text = 1;
+
+		if(have_text)
+		{
+			native_text = id3_field_getstrings(&pid3frame->fields[1], 0);
+
+			if(native_text)
+			{
+				have_utf8 = 1;
+				if(lang_index >= 0)
+					utf8_text = _get_utf8_text(native_text); // through iconv
+				else
+					utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text);
+
+				if(!strcmp(pid3frame->id, "TIT2"))
+				{
+					used = 1;
+					psong->title = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TPE1"))
+				{
+					used = 1;
+					psong->contributor[ROLE_ARTIST] = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TALB"))
+				{
+					used = 1;
+					psong->album = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TCOM"))
+				{
+					used = 1;
+					psong->contributor[ROLE_COMPOSER] = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TIT1"))
+				{
+					used = 1;
+					psong->grouping = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TPE2"))
+				{
+					used = 1;
+					psong->contributor[ROLE_BAND] = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TPE3"))
+				{
+					used = 1;
+					psong->contributor[ROLE_CONDUCTOR] = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TCON"))
+				{
+					used = 1;
+					psong->genre = (char*)utf8_text;
+					got_numeric_genre = 0;
+					if(psong->genre)
+					{
+						if(!strlen(psong->genre))
+						{
+							genre = WINAMP_GENRE_UNKNOWN;
+							got_numeric_genre = 1;
+						}
+						else if(isdigit(psong->genre[0]))
+						{
+							genre = atoi(psong->genre);
+							got_numeric_genre = 1;
+						}
+						else if((psong->genre[0] == '(') && (isdigit(psong->genre[1])))
+						{
+							genre = atoi((char*)&psong->genre[1]);
+							got_numeric_genre = 1;
+						}
+
+						if(got_numeric_genre)
+						{
+							if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
+								genre = WINAMP_GENRE_UNKNOWN;
+							free(psong->genre);
+							psong->genre = strdup(winamp_genre[genre]);
+						}
+					}
+				}
+				else if(!strcmp(pid3frame->id, "COMM"))
+				{
+					used = 1;
+					psong->comment = (char*)utf8_text;
+				}
+				else if(!strcmp(pid3frame->id, "TPOS"))
+				{
+					tmp = (char*)utf8_text;
+					strsep(&tmp, "/");
+					if(tmp)
+					{
+						psong->total_discs = atoi(tmp);
+					}
+					psong->disc = atoi((char*)utf8_text);
+				}
+				else if(!strcmp(pid3frame->id, "TRCK"))
+				{
+					tmp = (char*)utf8_text;
+					strsep(&tmp, "/");
+					if(tmp)
+					{
+						psong->total_tracks = atoi(tmp);
+					}
+					psong->track = atoi((char*)utf8_text);
+				}
+				else if(!strcmp(pid3frame->id, "TDRC"))
+				{
+					psong->year = atoi((char*)utf8_text);
+				}
+				else if(!strcmp(pid3frame->id, "TLEN"))
+				{
+					psong->song_length = atoi((char*)utf8_text);
+				}
+				else if(!strcmp(pid3frame->id, "TBPM"))
+				{
+					psong->bpm = atoi((char*)utf8_text);
+				}
+				else if(!strcmp(pid3frame->id, "TCMP"))
+				{
+					psong->compilation = (char)atoi((char*)utf8_text);
+				}
+			}
+		}
+
+		// check if text tag
+		if((!used) && (have_utf8) && (utf8_text))
+			free(utf8_text);
+
+		// v2 COMM
+		if((!strcmp(pid3frame->id, "COMM")) && (pid3frame->nfields == 4))
+		{
+			native_text = id3_field_getstring(&pid3frame->fields[2]);
+			if(native_text)
+			{
+				utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text);
+				if((utf8_text) && (strncasecmp((char*)utf8_text, "iTun", 4) != 0))
+				{
+					// read comment
+					free(utf8_text);
+
+					native_text = id3_field_getfullstring(&pid3frame->fields[3]);
+					if(native_text)
+					{
+						utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text);
+						if(utf8_text)
+						{
+							free(psong->comment);
+							psong->comment = (char*)utf8_text;
+						}
+					}
+				}
+				else
+				{
+					free(utf8_text);
+				}
+			}
+		}
+
+		index++;
+	}
+
+	id3_tag_delete(pid3tag);
+	free(id3tagbuf);
+	fclose(fp);
+	//DPRINTF(E_DEBUG, L_SCANNER, "Got id3tag successfully for file=%s\n", file);
+	return 0;
+}
+
+static int
+_get_dsffileinfo(char *file, struct song_metadata *psong)
+{
+	FILE *fp;
+	int len = 80;
+	unsigned char hdr[len];
+	uint32_t channelnum;
+	uint32_t samplingfrequency;
+	uint32_t bitpersample;
+	uint64_t samplecount;
+
+	if((fp = fopen(file, "rb")) == NULL)
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Could not create file handle\n");
+		return -1;
+	}
+
+	if(!(len = fread(hdr, len, 1,fp)))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Could not read chunks from %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+ 
+	if(strncmp((char*)hdr, "DSD ", 4))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Invalid DSD Chunk headerin %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+
+	if(strncmp((char*)hdr+28, "fmt ", 4))
+	{
+		DPRINTF(E_WARN, L_SCANNER, "Invalid fmt Chunk header in %s\n", file);
+		fclose(fp);
+		return -1;
+	}
+
+	channelnum = GET_DSF_INT32(hdr + 52);
+	samplingfrequency = GET_DSF_INT32(hdr + 56);
+	bitpersample = GET_DSF_INT32(hdr + 60);
+	samplecount = GET_DSF_INT64(hdr + 64);
+
+	psong->bitrate = channelnum * samplingfrequency * bitpersample;
+	psong->samplesize = bitpersample;
+	psong->samplerate = samplingfrequency;
+	psong->song_length = (samplecount / samplingfrequency) * 1000;
+	psong->channels = channelnum;
+
+	//DEBUG DPRINTF(E_INFO, L_SCANNER, "Got fileinfo successfully from =%s\n", file);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "bitrate is  %d\n", psong->bitrate);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "samplesize is  %d\n", psong->samplesize);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "samplerate is  %d\n", psong->samplerate);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "song_length is  %d\n", psong->song_length);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "channels are  %d\n", psong->channels);
+	//DEBUG DPRINTF(E_DEBUG, L_SCANNER, "samplecount are  %lld\n", samplecount);
+	fclose(fp);
+
+	xasprintf(&(psong->dlna_pn), "DSF");
+	return 0;
+}
diff --git a/tagutils/tagutils-dsf.h b/tagutils/tagutils-dsf.h
new file mode 100644
index 0000000..9b7634e
--- /dev/null
+++ b/tagutils/tagutils-dsf.h
@@ -0,0 +1,23 @@
+//=========================================================================
+// FILENAME	: tagutils-dsf.h
+// DESCRIPTION	: DSF metadata reader
+//=========================================================================
+// Copyright (c) 2014 Takeshich NAKAMURA
+//=========================================================================
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+static int _get_dsffileinfo(char *file, struct song_metadata *psong);
+static int _get_dsftags(char *file, struct song_metadata *psong);
diff --git a/tagutils/tagutils.c b/tagutils/tagutils.c
index 794e7b9..bb75eb6 100644
--- a/tagutils/tagutils.c
+++ b/tagutils/tagutils.c
@@ -107,6 +107,8 @@ char *winamp_genre[] = {
 #include "tagutils-asf.h"
 #include "tagutils-wav.h"
 #include "tagutils-pcm.h"
+#include "tagutils-dsf.h"
+#include "tagutils-dff.h"
 
 static int _get_tags(char *file, struct song_metadata *psong);
 static int _get_fileinfo(char *file, struct song_metadata *psong);
@@ -130,6 +132,8 @@ static taghandler taghandlers[] = {
 	{ "asf", 0,            _get_asffileinfo                                  },
 	{ "wav", _get_wavtags, _get_wavfileinfo                                  },
 	{ "pcm", 0,            _get_pcmfileinfo                                  },
+	{ "dsf", _get_dsftags, _get_dsffileinfo                                  },
+	{ "dff", 0,            _get_dfffileinfo                                  },
 	{ NULL,  0 }
 };
 
@@ -144,6 +148,8 @@ static taghandler taghandlers[] = {
 #include "tagutils-asf.c"
 #include "tagutils-wav.c"
 #include "tagutils-pcm.c"
+#include "tagutils-dsf.c"
+#include "tagutils-dff.c"
 #include "tagutils-plist.c"
 
 //*********************************************************************************
diff --git a/upnpglobalvars.h b/upnpglobalvars.h
index c4b9158..c7a7747 100644
--- a/upnpglobalvars.h
+++ b/upnpglobalvars.h
@@ -169,6 +169,7 @@
 	"http-get:*:audio/mp4:*," \
 	"http-get:*:audio/x-wav:*," \
 	"http-get:*:audio/x-flac:*," \
 	"http-get:*:audio/x-ape:*," \
+	"http-get:*:audio/x-dsd:*," \
 	"http-get:*:application/ogg:*"
 
 #define DLNA_FLAG_DLNA_V1_5      0x00100000
diff --git a/utils.c b/utils.c
index 5a233dc..d010b41 100644
--- a/utils.c
+++ b/utils.c
@@ -331,6 +331,8 @@ mime_to_ext(const char * mime)
 				return "3gp";
 			else if( strcmp(mime, "application/ogg") == 0 )
 				return "ogg";
+			else if( strcmp(mime+6, "x-dsd") == 0 )
+				return "dsd";
 			break;
 		case 'v':
 			if( strcmp(mime+6, "avi") == 0 )
@@ -398,7 +400,8 @@ is_audio(const char * file)
 		ends_with(file, ".m4a") || ends_with(file, ".aac")  ||
 		ends_with(file, ".mp4") || ends_with(file, ".m4p")  ||
 		ends_with(file, ".wav") || ends_with(file, ".ogg")  ||
-		ends_with(file, ".pcm") || ends_with(file, ".3gp"));
+		ends_with(file, ".pcm") || ends_with(file, ".3gp") ||
+		ends_with(file, ".dsf") || ends_with(file, ".dff"));
 }
 
 int
