view liboggplayer-src/src/play.cpp @ 14:083c73ceb716

Get rid of the vorbis related code.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 05 Aug 2013 16:54:46 +0300
parents 105513a2e3c9
children 3eacedd172ab
line wrap: on
line source

#include "imp.hpp"

bool OggPlayer::Imp::ready() {
	return audio_cache_ready;
}
// return true if need more data
// the return value is strange but the function is
// for internal use only and should only be called
// in play()
bool OggPlayer::Imp::decode_audio() {
#ifdef VORBIS_SUPPORT
	int ret;
	float **pcm;
	// if there's pending, decoded audio, grab it
	while ((ret = vorbis_synthesis_pcmout(&v_state, &pcm)) > 0) {
		boost::unique_lock<boost::mutex> lock(audio_mut);
		// first we need to convert from float to signed short
		// then we will use SDL_ConvertAudio
		// is our temp buffer big enough?
		if (cirbuf.capacity() < ret * v_info.channels * cvt.len_mult
			* sizeof(short)) {
				cirbuf.set_capacity(ret * v_info.channels * cvt.len_mult * 16
					* sizeof(short));
		}
		if (cirbuf.capacity() - cirbuf.size() < ret * v_info.channels
			* cvt.len_mult * sizeof(short)) {
				audio_cache_ready = true;
				return false;
		}
		if ((audio_buffer_size) < ret * v_info.channels * cvt.len_mult
			* sizeof(short)) {
				if (audio_buffer)
					delete[] audio_buffer;
				audio_buffer_size = ret * v_info.channels * cvt.len_mult
					* sizeof(short);
				audio_buffer = new char[audio_buffer_size];
		}
		// convert from float to short
		int k = 0;
		char* char_ab = (char*) audio_buffer;
		short* short_ab = (short*) audio_buffer;
		for (int i = 0; i < ret; i++) {
			for (int j = 0; j < v_info.channels; j++) {
				int val = (int)(pcm[j][i] * 32767.f);
				if (val > 32767)
					val = 32767;
				if (val < -32768)
					val = -32768;
				short_ab[k++] = val;

			}
		}
		vorbis_synthesis_read(&v_state, ret);
		// now we can use SDL_ConvertAudio
		cvt.len = k * sizeof(short);
		cvt.buf = (Uint8*) char_ab;
		SDL_ConvertAudio(&cvt);

		for (int i = 0; i < cvt.len_cvt; i++) {
			cirbuf.push_back(char_ab[i]);
		}


		if(v_state.granulepos!=0){
			AudioGranulePos agp;
			agp.pos = audio_bytes_played+cvt.len_cvt;
			agp.set_time = (double)v_state.granulepos/(double)v_info.rate;
			audio_granule_poses.push_back(agp);
		}

		lock.unlock();
		audio_ready_cond.notify_one();
		return false;

	} 
	// no pending audio; is there a pending packet to decode?
	if (ogg_stream_packetout(&o_vsstate, &o_packet) > 0) {
		if (vorbis_synthesis(&v_block, &o_packet) == 0) {
			vorbis_synthesis_blockin(&v_state, &v_block);
			return false;
		}
	} else { return true; }
#endif
	return true;
}
// similar to decode_audio
bool OggPlayer::Imp::decode_video() {
	bool was_ready=videobuf_ready;
	boost::unique_lock<boost::mutex> lock(video_mut, boost::defer_lock);
	ogg_int64_t videobuf_granulepos = -1;
	if (!videobuf_ready){
		if(!lock.try_lock())
			return false;
	}
	while (!videobuf_ready) {
		// theora is one in, one out...
		if (ogg_stream_packetout(&o_tsstate, &o_packet) > 0) {

			if(pp_inc){
				pp_level+=pp_inc;
				theora_control(&t_state,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
				pp_inc=0;
			}
			if (o_packet.granulepos >= 0) {
				theora_control(&t_state, TH_DECCTL_SET_GRANPOS,
					&o_packet.granulepos, sizeof(o_packet.granulepos));
			}
			if (theora_decode_packetin(&t_state, &o_packet) == 0) {
				videobuf_granulepos = t_state.granulepos;
				videobuf_time = theora_granule_time(&t_state,	videobuf_granulepos);

				if (videobuf_time >= get_time()){
					videobuf_ready = true;
					frame++;
				}
				else {
					// If we are too slow, reduce the pp level.
					pp_inc=pp_level>0?-1:0;
				}
			}

		} else
			break;
	}
	lock.unlock();
	if(videobuf_ready!=was_ready)
		video_ready_cond.notify_one();

	return !videobuf_ready;
}

void OggPlayer::Imp::play_loop() {
	if(!file_in.is_open()) return;
	audio_cache_ready = false;
	audio_bytes_played = 0;
	bool audio_need_data = vorbis_p;
	bool video_need_data = theora_p;
	timer.restart();
	// buffer_data() will close the file on eof
	while ((file_in.is_open() || !audio_need_data || !video_need_data) && playing) {

		// sync audio video timer
		while(!audio_granule_poses.empty() &&
			audio_granule_poses.front().pos <= audio_bytes_played){
			time_factor= audio_granule_poses.front().set_time/timer.elapsed();
			audio_granule_poses.pop_front();
		}

		if (theora_p && !videobuf_ready) {
			video_need_data = decode_video();
		}

		if (vorbis_p) {
			audio_need_data = decode_audio();
		}

		// are we at or past time for this video frame?
		if (videobuf_ready && videobuf_time <= get_time()) {
			videobuf_ready = false;
		}
		// if no data yet for somebody, grab another page
		if (file_in.is_open() && (audio_need_data || video_need_data)) {
			// buffer_data() can handle eof itself
			buffer_data();
			while (ogg_sync_pageout(&o_sync, &o_page) > 0) {
				queue_page(&o_page);
			}
			audio_need_data = false;
			video_need_data = false;
		}
	}
	playing = false;

	// do not risk a lock
	audio_ready_cond.notify_one();
	video_ready_cond.notify_one();

	// cleanup
	close();
}

void OggPlayer::Imp::play() {
	playing = true;
	play_thread = boost::thread(boost::bind(&OggPlayer::Imp::play_loop, this));
}