diff liboggplayer-src/src/play.cpp @ 2:105513a2e3c9

Import liboggplayer source.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 05 Aug 2013 13:50:20 +0300
parents
children 083c73ceb716
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboggplayer-src/src/play.cpp	Mon Aug 05 13:50:20 2013 +0300
@@ -0,0 +1,184 @@
+#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() {
+	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; }
+
+}
+// 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));
+}
+