Adding DTS support for B-series DTV

Ideas and dreaming will go this forum
Locked

smartsmurf
Official SamyGO Developer
Posts: 111
Joined: Thu Jun 24, 2010 8:26 am
Location: Frankfurt, Germany

Adding DTS support for B-series DTV

Post by smartsmurf »

Some weeks ago I started the project of adding DTS support to samsung DTV since my B650 can not handle it.
According to the rules (http://forum.samygo.tv/viewtopic.php?f=5&t=607) I will describe and explain my hack. :)
It will look easier than it actually had been, because I will leave out all the dead ends I went into... ;)

At the beginning of this little project the first issue that I encountered was the fact that the builtin ffmpeg libray (version 0.4) lacks DTS/DCA support. This can be seen when loading an DTS movie. You will get something like this:

Code: Select all

===============[DUMP FORMAT]==============

* [FFMPEG LIBRARY] Ver. 2008/11/24(^__^) modified by C.Lucifer (0.4) *
Input #0, mpeg, from '/mtd_wiselink/MOVIE001/dts_experience.vob':
Duration: 00:01:17.17, start: 0.233567, bitrate: 8692 kb/s
Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x480 [PAR 8:9 DAR 4:3], 7000 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc
Stream #0.1[0x88]: Audio: dca, 0 Hz, 5.1, s16
Obviously the sample rate (should be 48000) is not detected correctly. So the first requirement is to replace the library with an external one.
The class "CParsingMedia" inside exeDSP is the interface beween Samsung code and ffmpeg library. The method "CParsingMedia::PrepareParsing()" loads the modules and gets required symbols.

I compiled one of the latest ffmpeg libraries (0.6), optimized DCA stuff and copied the modules into /mtd_rwarea/lib. Then I replaced "CParsingMedia::PrepareParsing()" with the following function:

Code: Select all

#define FFMPEG_LIB_PATH "/mtd_rwarea/lib/"

/* use external FFMPEG libraries */
void CParsingMedia::PrepareParsingExtFfmpeg(void)
{
	ROSE_PRINT(0x4,"[CParsingMedia] %s In", __FUNCTION__ );

	hLib_avutil		= dlopen( FFMPEG_LIB_PATH "libavutil.so.50", RTLD_LAZY );
	dlError			= dlerror();
	if( hLib_avutil == NULL ){
		ROSE_PRINT(0x4000,"[libavutil.so dlopen error]  %s", dlError );
		goto exit_close_avutil;
	}
	/* ffmpeg 0.6 */
	hLib_avcore		= dlopen( FFMPEG_LIB_PATH "libavcore.so.0", RTLD_LAZY );
	dlError			= dlerror();
	if( hLib_avcore == NULL ){
		ROSE_PRINT(0x4000,"[libavcore.so.0 dlopen error]  %s", dlError );
		// goto exit_close_avutil;
	}
	hLib_avcodec	= dlopen( FFMPEG_LIB_PATH "libavcodec.so.52", RTLD_LAZY );
	dlError			= dlerror();
	if( hLib_avcodec == NULL ){
		ROSE_PRINT(0x4000,"[libavcodec.so dlopen error]  %s", dlError );
		goto exit_close_avcodec;
	}
	hLib_avformat	= dlopen( FFMPEG_LIB_PATH "libavformat.so.52", RTLD_LAZY );
	dlError			= dlerror();
	if( hLib_avformat == NULL ){
		ROSE_PRINT(0x4000,"[libavformat.so dlopen error]  %s", dlError );
		goto exit_close_avformat;
	}

	av_register_all222 = (av_register_all_t)dlsym(hLib_avformat,"av_register_all");
	if( av_register_all222 == NULL ){
		ROSE_PRINT(0x4000,"[av_register_all222 dlsym error]");
		goto exit_close_avformat;
	}
	av_open_input_file222 = (av_open_input_file_t)dlsym(hLib_avformat,"av_open_input_file");
	if( av_open_input_file222 == NULL ){
		ROSE_PRINT(0x4000,"[av_open_input_file222 dlsym error]");
		goto exit_close_avformat;
	}
	av_close_input_file222 = (av_close_input_file_t)dlsym(hLib_avformat,"av_close_input_file");
	if( av_close_input_file222 == NULL ){
		ROSE_PRINT(0x4000,"[av_close_input_file222 dlsym error]");
		goto exit_close_avformat;
	}
	av_find_stream_info222 = (av_find_stream_info_t)dlsym(hLib_avformat,"av_find_stream_info");
	if( av_find_stream_info222 == NULL ){
		ROSE_PRINT(0x4000,"[av_find_stream_info222 dlsym error]");
		goto exit_close_avformat;
	}
	dump_format222 = (dump_format_t)dlsym(hLib_avformat,"dump_format");
	if( dump_format222 == NULL ){
		ROSE_PRINT(0x4000,"[dump_format222 dlsym error]");
		goto exit_close_avformat;
	}
	getMpeg4AACObjetType222 = (getMpeg4AACObjetType_t)dlsym(hLib_avformat,"getMpeg4AACObjetType");
	if( getMpeg4AACObjetType222 == NULL ){
		ROSE_PRINT(0x4000,"[getMpeg4AACObjetType222 dlsym error]");
		goto exit_close_avformat;
	}
	getMpeg4AACSBR222 = (getMpeg4AACSBR_t)dlsym(hLib_avformat,"getMpeg4AACSBR");
	if( getMpeg4AACSBR222 == NULL ){
		ROSE_PRINT(0x4000,"[getMpeg4AACSBR222 dlsym error]");
		goto exit_close_avformat;
	}

	/* all OK */
	ROSE_PRINT(0x4,"[CParsingMedia] %s Out", __FUNCTION__ );
	return;

exit_close_avformat:
	dlclose( hLib_avformat );
exit_close_avcodec:
	dlclose( hLib_avcodec );
exit_close_avutil:
	dlclose( hLib_avutil );
	return;
}
Finally I was able to get the AVFormatContext populated properly, and also the dump information shows up correctly:

Code: Select all

===============[DUMP FORMAT]==============
Input #0, mpeg, from '/mtd_wiselink/MOVIE001/dts_experience.vob':
  Duration: 00:01:17.17, start: 0.233567, bitrate: 8692 kb/s
    Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x480 [PAR 8:9 DAR 4:3], 7000 kb/s, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc
    Stream #0.1[0x88]: Audio: dca, 48000 Hz, 5.1, s16, 1536 kb/s
========================================== 
The backside of this medal: at least it breaks MKV support since the internal structures changed heavily from ffmpeg 0.4 to 0.6 and the Samsung engineers use these structures within "CParsingMedia::SetAudioStreamInfo()" and "CParsingMedia::SetVideoStreamInfo()".

So finally I ended up in re-writing almost the complete class "CParsingMedia"...
smartsmurf
Official SamyGO Developer
Posts: 111
Joined: Thu Jun 24, 2010 8:26 am
Location: Frankfurt, Germany

Adding DTS support for B-series DTV (2)

Post by smartsmurf »

I don't know the reason, but audio processing while movie playback is done through the so-called AIDA audio engine. Amongst few others it supports the PCM format. That's why I decided to give downmixing DTS to 2-channel PCM a try. There are probably several ways to achieve this, but I chose to add some code to the "CMovieCore::SetMediaInfo()" method (see preprocessor directive __DCATOPCM__):

Code: Select all

int CMovieCore::SetMediaInfo(Multimedia::_tMediaInfoContext * media_info)
{
	int target_video_stream;
	int target_audio_stream;
	int counter_video_streams;
	int counter_audio_streams;
	int frame_width;
	int frame_height;
	float frame_rate;
	SdVideoFormat_k videoformat_TD;
	SdAudioFormat_k audioformat_TD;
	int sample_rate;
	int audio_index;
	int video_index;
	CMovieTDIF::_tSetAudioPCM SettingsPCM;
	CMovieTDIF::_tSetAudioWMA SettingsWMA;
	CMovieTDIF::_tSetAudioAAC SettingsAAC;

	PRINTF(FUNC_MOVIECORE_IN, __FUNCTION__);

	if( media_info->nVideoStream > 0 ){
		target_video_stream = 1;
		target_audio_stream = 1;
	} else {
		target_audio_stream = media_info->nAudioStream;
		target_video_stream = media_info->nVideoStream;
	}

	memset( (void *)&SettingsPCM, 0, sizeof(CMovieTDIF::_tSetAudioPCM) );
	memset( (void *)&SettingsWMA, 0, sizeof(CMovieTDIF::_tSetAudioWMA) );
	memset( (void *)&SettingsAAC, 0, sizeof(CMovieTDIF::_tSetAudioAAC) );

	if( media_info->nStreams > 0 ){
		counter_audio_streams = 0;
		frame_height = 0;
		frame_rate = 0;
		counter_video_streams = 0;
		frame_width = 0;
		videoformat_TD = SDVIDEOFORMAT_NONE;
		audioformat_TD = SDAUDIOFORMAT_NONE;
		sample_rate = 0;
		audio_index = 0;
		video_index = 0;

		for(int i = 0; i < media_info->nStreams; i++ ){
			switch( media_info->stream_info[i].stream_type ){
				case Multimedia::STREAM_TYPE_VIDEO:
					counter_video_streams++;
					if( (counter_video_streams == target_video_stream) || 
						(media_info->stream_info[i].stream_id > video_index ) ){
						video_index = media_info->stream_info[i].stream_id;
						videoformat_TD = VideoFormatToTD(media_info->stream_info[i].video_info.VideoCodecID,
							media_info->stream_info[i].video_info.nSubId);
						frame_width = media_info->stream_info[i].video_info.nFrameWidth;
						frame_height = media_info->stream_info[i].video_info.nFrameHeight;
						frame_rate = media_info->stream_info[i].video_info.nFrameRate;
						ROSE_PRINT(0x4, "In handleMediaInfo_forSDAL, pMediaInfo->tStreams[nIndex].tVideoInfo.nPid = 0x%x",
							media_info->stream_info[i].video_info.nPid );
					}
					break;
				case Multimedia::STREAM_TYPE_AUDIO:
					counter_audio_streams++;
					if( (counter_audio_streams == target_audio_stream) || 
						(media_info->stream_info[i].stream_id > audio_index ) ){
						audio_index = media_info->stream_info[i].stream_id;
						audioformat_TD = AudioFormatToTD(media_info->stream_info[i].audio_info.AudioCodecID);
						sample_rate = media_info->stream_info[i].audio_info.nSampleRate;
						switch( audioformat_TD ){
						case SDAUDIOFORMAT_PCM:	// 0x1:
						case SDAUDIOFORMAT_ADPCM:	// 0x2:
#ifdef __DCATOPCM__
							soft_codec_id = CODEC_ID_NONE;
#endif
							SettingsPCM.eFsRate = ConvertSampleRateToTD(sample_rate);
							if( media_info->stream_info[i].audio_info.nChannel && 
								sample_rate ){
								SettingsPCM.bitWidth = media_info->stream_info[i].audio_info.nBitRate / sample_rate / media_info->stream_info[i].audio_info.nChannel;
							}
							SettingsPCM.channels = media_info->stream_info[i].audio_info.nChannel;
							SettingsPCM.bLittleEndian = 1;
							if( audioformat_TD == SDAUDIOFORMAT_ADPCM ){
								SettingsPCM.blockAlign = media_info->stream_info[i].audio_info.nBlockAlign;
								SettingsPCM.eADPCM = AudioADPCMFormatToTD(media_info->stream_info[i].audio_info.nSubId);
							} else {
								SettingsPCM.blockAlign = 0;
								SettingsPCM.eADPCM = 0;
								SettingsPCM.ePCM = AudioPCMFormatToTD(media_info->stream_info[i].audio_info.nSubId);
							}
							break;
						case SDAUDIOFORMAT_AAC: // 0x6:
						case SDAUDIOFORMAT_HEAAC: // 0x8:
#ifdef __DCATOPCM__
							soft_codec_id = CODEC_ID_NONE;
#endif
							SettingsAAC.type = 0x4;
							SettingsAAC.samplerate_index = AudioAACSampeRateIndex(sample_rate);
							SettingsAAC.sub_id = media_info->stream_info[i].audio_info.nSubId;
							SettingsAAC.channels = media_info->stream_info[i].audio_info.nChannel;
							SettingsAAC.sample_rate = sample_rate;
							SettingsAAC.e8 = 0;
							SettingsAAC.e12 = 0;
							SettingsAAC.e13 = 0;
							SettingsAAC.e14 = 0;
							SettingsAAC.e15 = 0;
							if( SettingsAAC.sub_id == 0x5 ){
								SettingsAAC.e17 = media_info->stream_info[i].audio_info.mRes4;
								SettingsAAC.e18 = media_info->stream_info[i].audio_info.mRes5;
								SettingsAAC.e16 = 1;
							} else {
								SettingsAAC.e17 = 0;
								SettingsAAC.e18 = 0;
								SettingsAAC.e16 = 0;
							}
							break;
						case SDAUDIOFORMAT_WMA: // 0xC:
#ifdef __DCATOPCM__
							soft_codec_id = CODEC_ID_NONE;
#endif
							SettingsWMA.codec_tag = media_info->stream_info[i].audio_info.nCodecTag;
							SettingsWMA.bits_per_sample = media_info->stream_info[i].audio_info.nValidBitsPerSample;
							SettingsWMA.channels = media_info->stream_info[i].audio_info.nChannel;
							SettingsWMA.byte_rate = (media_info->stream_info[i].audio_info.nBitRate+7) >> 3;
							SettingsWMA.block_align = media_info->stream_info[i].audio_info.nBlockAlign << 3;
							SettingsWMA.sample_rate = sample_rate;
							break;
#ifdef __DCATOPCM__
						case SDAUDIOFORMAT_DTS:	// 0xA:
							PRINTF("%s: Setting PCM values for DCA audio.", __FUNCTION__ );
							soft_codec_id = CODEC_ID_DTS;
							sample_rate = 48000;
							SettingsPCM.eFsRate = ConvertSampleRateToTD(sample_rate);
							SettingsPCM.bitWidth = 16;
							SettingsPCM.channels = 2;
							SettingsPCM.bLittleEndian = 1;
							audioformat_TD = SDAUDIOFORMAT_PCM;
							SettingsPCM.blockAlign = 0;
							SettingsPCM.eADPCM = 0;
							SettingsPCM.ePCM = 0; // AudioPCMFormatToTD(CODEC_ID_PCM_S16BE);
							break;
#endif
						default:
							break;
						}
					}
					break;
				default:
					break;
			}
		}
		m_MovieSettings.ContainerFormat_TD = ContainerTypeToTD(media_info->container_info.nId);
		if( m_MovieSettings.ContainerFormat_TD == SDCONTAINERFORMAT_NONE ){
			ROSE_PRINT(0x4004, "TD_CONTAINER_NONE");
			return 0;
		}
		m_MovieSettings.mRes0B8 = mRes0E8;
		m_MovieSettings.VideoFormat_TD = videoformat_TD;
		if( m_MovieSettings.VideoFormat_TD == 0 ){
			ROSE_PRINT(0x4004, "TD_VIDEO_FORMAT_NONE");
			return 0;
		}
		m_MovieSettings.AudioFormat_TD = audioformat_TD;
		m_MovieSettings.SampleRate_TD = ConvertSampleRateToTD(sample_rate);
		m_MovieSettings.Filesize = media_info->file_info.filesize;
		memcpy( (void *)&m_MovieSettings.eSettingsPCM, (void *)&SettingsPCM, sizeof(CMovieTDIF::_tSetAudioPCM) );
		memcpy( (void *)&m_MovieSettings.eSettingsAAC, (void *)&SettingsAAC, sizeof(CMovieTDIF::_tSetAudioAAC) );
		memcpy( (void *)&m_MovieSettings.eSettingsWMA, (void *)&SettingsWMA, sizeof(CMovieTDIF::_tSetAudioWMA) );
		m_MovieSettings.Duration = media_info->container_info.nTotalPlayTime * 1000;
		m_MovieSettings.FrameRate = frame_rate;
		m_MovieSettings.FrameWidth = frame_width;
		m_MovieSettings.FrameHeight = frame_height;
		return 1;
	} else {
		frame_height = 0;
		frame_rate = 0;
		frame_width = 0;
		videoformat_TD = SDVIDEOFORMAT_NONE;
		audioformat_TD = SDAUDIOFORMAT_NONE;
		sample_rate = 0;
		return 0;
	}
}
It just tricks the audio engine to initialize for little-endian, signed 16-bit PCM data. Once reached this stage DTS audio frames will be sent through PCM processing code which results in quite noisy sound. But it is important to see: audio decoding is initialized. So the next step can follow...
User avatar
erdem_ua
SamyGO Admin
Posts: 3125
Joined: Thu Oct 01, 2009 6:02 am
Location: Istanbul, Turkey
Contact:

Re: Adding DTS support for B-series DTV

Post by erdem_ua »

This thread is just for SmartSmurf, don't post here. Thanks
smartsmurf
Official SamyGO Developer
Posts: 111
Joined: Thu Jun 24, 2010 8:26 am
Location: Frankfurt, Germany

Re: Adding DTS support for B-series DTV

Post by smartsmurf »

After having redirected DTS into PCM mode, exeDSP will select the audio codec functions for PCM. All methods are exported at gAvfdInterfaceAudioCodecFunctions.

Some functions need to be replaced:

Code: Select all

/* audio driver */
struct AVFD_AudioInterface_t {
	int  (*Open)(int, struct avfd_interface_data_t **);					// 00
	int  (*Close)(void *);												// 01
	int  (*AudioTransferData)(struct avfd_instance_t *, int, int);		// 02
	int  (*AudioTransferDataWithPESHdr)(void *);	// 03
	int  (*AudioTransferBufDataWithPESHdr)(void *);	// 04
	...
	int  (*InterfacerContainerAeCopyData)(struct avfd_instance_t *, unsigned char *, unsigned int);	// 18
	...
}; 
The Open() and Close() functions will be the first to get patched. They need code to create and to destroy the ffmpeg context:

Code: Select all

/* The extension to the original spIAe_Open function */
int spIAe_Open_DTS( struct avfd_instance_t * avfd , void * result )
{

	PRINTF("%s In", __FUNCTION__);

	/* Initialize ffmpeg decoder, if required */
	if( soft_codec_id != CODEC_ID_NONE ){
	
		ffmpeg.av_init_packet(&avpacket);

		/* find the requested audio decoder */
		pCodec = ffmpeg.avcodec_find_decoder(soft_codec_id);
		if( !pCodec ){
			LOG("Error: required codec (0x%X) not found. Please check your ffmpeg library.", soft_codec_id);
			return 0x8522DFF9;
		}
	    pCodecCtx = ffmpeg.avcodec_alloc_context();

		/* enable stereo downmix */
		pCodecCtx->request_channels = 2;

		/* open it */
		if( ffmpeg.avcodec_open(pCodecCtx, pCodec) < 0 ){
			LOG("Error: could not open codec");
			return 0x8522DFF9;
		}

		/* TODO: create the resample context(s) */
//		resample = ffmpeg.av_audio_resample_init( 2, 6, 48000, 48000,
//			SAMPLE_FMT_S16, SAMPLE_FMT_S16, 16, 10, 0, 0.8);
//		if( !resample ){
//			LOG("Error: could not create resample context.");
//			return 0x8522DFF9;
//		}

		/* framebuf contains audio data from feeder */
	    framebuf = (unsigned char *)malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
		/* samples will contain decoded audio samples @ 48000 Hz */
		samples = (short *)malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);

		/* just for debugging, a sample counter */
		total_samples = 0;
	}

	/* Continue with original spIAe_Open() function */
	return spIAe_Open( avfd, result );
}


/* The extension to the original spIAe_Close function */
int spIAe_Close_DTS( struct avfd_instance_t * avfd )
{
	PRINTF("%s In", __FUNCTION__);
	
	/* TODO: close the resample context(s) */
//	ffmpeg.audio_resample_close( resample );
	if( framebuf )
		free( framebuf );
	framebuf = NULL;
	if( samples )
		free( samples );
    samples = NULL;
	if( pCodecCtx ){
		ffmpeg.avcodec_close( pCodecCtx );
	    free( pCodecCtx );
		pCodecCtx = NULL;
	}
	/* Continue with original spIAe_Close() function */
	return spIAe_Close( avfd );
}
At this stage almost everything is set up. So next step will be transcoding DTS into PCM frames.
User avatar
erdem_ua
SamyGO Admin
Posts: 3125
Joined: Thu Oct 01, 2009 6:02 am
Location: Istanbul, Turkey
Contact:

Re: Adding DTS support for B-series DTV

Post by erdem_ua »

I approve hack's binary. I know that it's possible to play DTS videos on B series TV.
Binary released after some work.
Thanks for SmartSmurf for this hack and also for other works.
Erdem
smartsmurf
Official SamyGO Developer
Posts: 111
Joined: Thu Jun 24, 2010 8:26 am
Location: Frankfurt, Germany

Re: Adding DTS support for B-series DTV

Post by smartsmurf »

At the moment I fix up some code and also try different optimizations to ffmpeg library. Additionally I try whether libdca is useful in terms of performance. I propose to release sources and binaries on this forum in the night from 19th to 20th of November 2010. Please be patient... :)
smartsmurf
Official SamyGO Developer
Posts: 111
Joined: Thu Jun 24, 2010 8:26 am
Location: Frankfurt, Germany

Re: Adding DTS support for B-series DTV

Post by smartsmurf »

The application SamyGO DCA has been released here:
https://downloads.sourceforge.net/proje ... %200.1.zip

You can find the GPL'd sources here:
https://downloads.sourceforge.net/proje ... .1_src.zip

Good luck!

Btw, the module uses both libdca and ffmpeg for decoding with precedence for libdca. In order to use the DCA decoding from ffmpeg, just delete "libdca.so" from the folder.
User avatar
erdem_ua
SamyGO Admin
Posts: 3125
Joined: Thu Oct 01, 2009 6:02 am
Location: Istanbul, Turkey
Contact:

Re: Adding DTS support for B-series DTV

Post by erdem_ua »

It's good I will re-release same binary with native format and renaming your one as "Restricted Firmware" release. Because all other packages are in same structure.
And I tried at all of my DTS trailers. It could open every DTS file.

All users should know that, DTS are decompressed on processor and consume your TV's CPU. So your Remote controller will be less responsive. Know that.
And don't try to run this extension from USB. It probably reboots TV due USB speed. Instead copy it to TV's internal memory first!
Thanks SmartSmurf. You are very good on this job. I donated my own project too for increase bounty. ;)

WARNING!: NUKE RELEASED AND COMING A HEAD! ALL YOUR POSTS WILL BE DELETED!
This thread is not for comment & bug-reports. Please open a thread at support forum and use there. Here is for only SmartSmurf and Me!

Okey Deactivated nuke. Just split this thread.
Please follow this topic for questions,bug reports or clapping :)
smartsmurf
Official SamyGO Developer
Posts: 111
Joined: Thu Jun 24, 2010 8:26 am
Location: Frankfurt, Germany

Re: Adding DTS support for B-series DTV

Post by smartsmurf »

Here comes the last part of the source code explanation...

Each audio frame will be sent through the interface->AudioTransferData (for MPEG & VOB) or interface->AudioTransferDataWithPESHdr (for MKV). So we need to put our replacement functions in here:

Code: Select all

int InterfacerAudioTransferData_DTS(struct avfd_instance_t * avfd, int buffsize, int * param3 )
{
	int len;
	int out_size;

	/* when there is no need for software decoding,
	   just pass data to original funtion */
	if( soft_codec_id == CODEC_ID_NONE )
		return uldAvfd_InterfacerAudioTransferData( avfd, buffsize, param3 );

	/* according to disassembly this case will never happen (param3 is always 0).
	   checked with T-CHLCIPDEUC2007.1 */
	if( param3 ){
		if( (param3[1]) &&
			(param3[0]) ){
			PRINTHEX( "data (1):", (unsigned char *)&param3[0], param3[1] );
			uldAvfd_InterfacerContainerAeCopyData( avfd, (unsigned char *)&param3[0], param3[1] );
		}
	}

	if( framebuf == NULL ){
		PRINTF("[%s] error: framebuf is NULL!", __FUNCTION__ );
		return 0;
	}

	/* read and ignore audio substream header */
	avfd_GetBuf(avfd->StreamHandle, framebuf, 3 );
	buffsize -= 3;
	/* read DTS frame (should start with DTS sync word 7FFE8001) */
	avfd_GetBuf(avfd->StreamHandle, framebuf, buffsize );

	if( samygodca_use_libdca )
		out_size = libdca_dcatopcm( samples, framebuf, buffsize, 1 );
	else
		out_size = ffmpeg_dcatopcm( samples, framebuf, buffsize, 1 );

	if( audio_header_size && out_size ){

		unsigned char lpcm_header[6];
		unsigned char audio_index;

		// de-mask substream number	
		audio_index = audio_header[audio_header_size - 1] & 7;
		if( audio_header[14] == 0xFF ){
			audio_header[8] = 0x06;
		} else {
			audio_header[8]  = 0x09;
			audio_header[14] = 0x10;
			audio_header[15] = 0x60;
			audio_header[16] = 0x08;	// org: 0x04
			audio_header[17] = 0xFF;
		}

		audio_header_size = audio_header[8]+10;

		/* adjust len according to payload */
		len = out_size + audio_header_size; // + 6;

		audio_header[4] = (unsigned char)((len >> 8) & 0xFF);
		audio_header[5] = (unsigned char)((len) & 0xFF);
		/* insert new substream number or 0xA0 (LPCM substream number 0)*/
		audio_header[audio_header_size-1] = 0xA0 | audio_index;
		lpcm_header[0] = 0x07;	/* number of frames */
		lpcm_header[1] = 0x00;	/* 0004 */
		lpcm_header[2] = 0x04;	/* 0004 -> PTS frame @ offset 26 */
		lpcm_header[3] = 0x0C;	/* emphasis off (128), mute off (64), frame modulo 20 = 0x0C */
		lpcm_header[4] = 0x01;	/* 16-bit, 48K sample rate, stereo */
		lpcm_header[5] = 0x80;	/* dynamic range 128 (neutral) */

		uldAvfd_InterfacerContainerAeCopyData( avfd, audio_header, audio_header_size );
		memcpy( framebuf, lpcm_header, sizeof(lpcm_header) );
		memcpy( framebuf+sizeof(lpcm_header), (void *)samples, out_size );
		staAvfd_AeCopyData( avfd, framebuf, out_size + sizeof(lpcm_header) );
		audio_header_size = 0;
		return 0;
	} else {
		return 0; // return uldAvfd_InterfacerContainerAeCopyData( avfd, trans_buf, buffsize );
	}
}

int InterfacerAudioTransferDataWithSyncHdr_DTS( struct avfd_instance_t * avfd, int param2, int param3, int param4, int param5)
{
	int out_size;
	unsigned char temp_header[sizeof(audiotransfer_sync_header)];

	/* when there is no need for software decoding,
	   just pass data to original funtion */
	if( soft_codec_id == CODEC_ID_NONE )
		return uldAvfd_InterfacerAudioTransferDataWithSyncHdr( avfd, param2, param3, param4, param5);

	if( framebuf == NULL ){
		PRINTF("[%s] error: framebuf is NULL!", __FUNCTION__ );
		return 0;
	}

	avfd_GetBuf(avfd->StreamHandle, framebuf, param2 );

	if( samygodca_use_libdca )
		out_size = libdca_dcatopcm( samples, framebuf, param2, 0 );
	else
		out_size = ffmpeg_dcatopcm( samples, framebuf, param2, 0 );

	memcpy( temp_header, audiotransfer_sync_header, sizeof(audiotransfer_sync_header) );

	if( param3 == -1 ){
		temp_header[0x08] = 0xFF;
		temp_header[0x09] = 0xFF;
		temp_header[0x0A] = 0xFF;
		temp_header[0x0B] = 0xFF;
	} else {
		temp_header[0x08] = (unsigned char)(param3 >> 25);
		temp_header[0x09] = (unsigned char)(param3 >> 17);
		temp_header[0x0A] = (unsigned char)(param3 >> 9);
		temp_header[0x0B] = (unsigned char)(param3 >> 1);
	}
	temp_header[0x0C] = (unsigned char)(out_size >> 24);
	temp_header[0x0D] = (unsigned char)(out_size >> 16);
	temp_header[0x0E] = (unsigned char)(out_size >> 8);
	temp_header[0x0F] = (unsigned char)(out_size);
	if( param5 == 0 )
		uldAvfd_InterfacerContainerAeCopyData( avfd, temp_header, sizeof(temp_header) );

	if( out_size ){
		return staAvfd_AeCopyData( avfd, (unsigned char *)samples, out_size );
	}
	return 0;
}
Then we need to include libdca and ffmpeg in order to transform DCA into PCM:

Code: Select all

static int libdca_dcatopcm( short * pcm_out, unsigned char * dca_in, int dca_in_size, int big_endian )
{
    int sample_rate;
    int frame_length;
    int flags;
	int bit_rate;
	int length;
	level_t level;
	sample_t bias; 
	int i;
	int out_size = 0;
	int num_blocks;
	short * pcm_out_ptr = pcm_out;

	length = libdca.dca_syncinfo( libdca_state, dca_in, &flags, &sample_rate, &bit_rate, &frame_length );
	if (!length) {
		LOG("Error returned by dca_syncinfo()");
		PRINTHEX("DCA Frame data/32:", dca_in, 32 );
		return 0;
	}
	flags = DCA_STEREO;
#ifdef LIBDCA_FIXED
	level = (1 << 26);	/* see libao/audio_out_internal.h  */
	bias  = 0;			/* see libao/audio_out_internal.h  */
#else
	level = 1;			/* see libao/audio_out_internal.h  */
	bias  = 384;		/* see libao/audio_out_internal.h  */
#endif
	if( libdca.dca_frame( libdca_state, dca_in, &flags, &level, bias) ){
		LOG("Error returned by dca_frame()");
		PRINTHEX("DCA Frame data/32:", dca_in, 32 );
		return 0;
	}
	/* disable dynrng */
	libdca.dca_dynrng( libdca_state, NULL, NULL );

	num_blocks = libdca.dca_blocks_num(libdca_state);
	for (i = 0; i < num_blocks; i++) {
		if( libdca.dca_block(libdca_state) ){
			LOG("Error returned by dca_block()");
			return 0;
		}
		// libdca_sample_to_short( pcm_out_ptr, libdca.dca_samples(libdca_state), 256 );
		libdca_convert2s16_2( libdca.dca_samples(libdca_state), pcm_out_ptr );
		pcm_out_ptr += 512;
		out_size += 1024;
	}

	/* increment number of samples */
	total_samples += (out_size >> 1);

	if( big_endian )
		change_endian16( pcm_out, pcm_out, out_size >> 1 );	

	return out_size;
}

static int ffmpeg_dcatopcm( short * pcm_out, unsigned char * dca_in, int dca_in_size, int big_endian )
{
	int len;
	int out_size;

    avpacket.data = framebuf;
    avpacket.size = dca_in_size;
    out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
    len = ffmpeg.avcodec_decode_audio3(pCodecCtx, samples, &out_size, &avpacket);
    if( len < 0 ){
		LOG("Error while decoding");
		PRINTHEX("DCA Frame data/32:", dca_in, 32 );
		return 0;
    }
    if( len != dca_in_size ){
		LOG("Not entire buffer was decoded (%i of %i)", len, dca_in_size );
    }

	if( (pCodecCtx->sample_rate != 48000) ||
		(pCodecCtx->bit_rate != 1536000) )
		PRINTF("Decoded DCA packet needs resampling (pCodecCtx->bit_rate=%i, pCodecCtx->sample_rate=%i)", pCodecCtx->bit_rate, pCodecCtx->sample_rate );

	/* increment number of samples */
	total_samples += (out_size >> 1);

	/* PCM_S16BE "encoding" -> change from LE16 to BE16 */
	if( big_endian )
		change_endian16( pcm_out, pcm_out, out_size >> 1 );

	return out_size;
}

Locked

Return to “[B] Brainstorm”