2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
|
|
#include <libavformat/avformat.h>
|
|
|
|
#include <libavdevice/avdevice.h>
|
|
|
|
#include <libswscale/swscale.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <kos32sys.h>
|
2014-08-07 00:17:00 +02:00
|
|
|
#include "winlib/winlib.h"
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
#include "sound.h"
|
|
|
|
#include "fplay.h"
|
|
|
|
|
|
|
|
volatile enum player_state player_state = STOP;
|
|
|
|
volatile enum player_state decoder_state = PREPARE;
|
|
|
|
volatile enum player_state sound_state = STOP;
|
|
|
|
|
|
|
|
uint32_t win_width, win_height;
|
|
|
|
|
2016-02-01 20:27:17 +01:00
|
|
|
uint8_t *decoder_buffer;
|
2014-01-10 14:47:21 +01:00
|
|
|
extern int resampler_size;
|
|
|
|
extern int sample_rate;
|
2016-02-01 16:10:02 +01:00
|
|
|
|
2014-01-10 14:47:21 +01:00
|
|
|
int64_t rewind_pos;
|
|
|
|
|
|
|
|
int64_t stream_duration;
|
|
|
|
|
2016-11-03 11:00:13 +01:00
|
|
|
volatile int threads_running = DECODER_THREAD;
|
2016-02-01 16:10:02 +01:00
|
|
|
|
2014-01-10 14:47:21 +01:00
|
|
|
int main( int argc, char *argv[])
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
static vst_t vst;
|
|
|
|
int i, ret;
|
2014-01-10 14:47:21 +01:00
|
|
|
char *file_name, *dot;
|
|
|
|
|
|
|
|
if(argc < 2)
|
|
|
|
{
|
2016-02-04 20:25:52 +01:00
|
|
|
vst.input_file = get_moviefile();
|
|
|
|
if(vst.input_file == NULL)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
printf("Please provide a movie file\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2016-02-04 20:25:52 +01:00
|
|
|
else vst.input_file = argv[1];
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
/* register all codecs, demux and protocols */
|
|
|
|
|
|
|
|
av_log_set_level(AV_LOG_FATAL);
|
|
|
|
|
|
|
|
avcodec_register_all();
|
|
|
|
avdevice_register_all();
|
|
|
|
av_register_all();
|
|
|
|
|
2016-02-04 20:25:52 +01:00
|
|
|
if( avformat_open_input(&vst.fCtx, vst.input_file, NULL, NULL) < 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-04 20:25:52 +01:00
|
|
|
printf("Cannot open file %s\n\r", vst.input_file);
|
2014-01-10 14:47:21 +01:00
|
|
|
return -1; // Couldn't open file
|
|
|
|
};
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
vst.fCtx->flags |= AVFMT_FLAG_GENPTS;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
if(avformat_find_stream_info(vst.fCtx, NULL) < 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
printf("Cannot find streams\n\r");
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
|
2016-02-04 20:25:52 +01:00
|
|
|
file_name = strrchr(vst.input_file,'/')+1;
|
2014-01-10 14:47:21 +01:00
|
|
|
dot = strrchr(file_name,'.');
|
|
|
|
if(dot)
|
|
|
|
{
|
2016-02-04 20:25:52 +01:00
|
|
|
vst.input_name = malloc(dot-file_name+1);
|
|
|
|
memcpy(vst.input_name, file_name, dot-file_name);
|
|
|
|
vst.input_name[dot-file_name] = 0;
|
2014-01-10 14:47:21 +01:00
|
|
|
}
|
2016-02-04 20:25:52 +01:00
|
|
|
else vst.input_name = file_name;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
stream_duration = vst.fCtx->duration;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
vst.vStream = -1;
|
|
|
|
vst.aStream = -1;
|
|
|
|
|
|
|
|
for(i=0; i < vst.fCtx->nb_streams; i++)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
if(vst.fCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO
|
|
|
|
&& vst.vStream < 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
vst.vStream = i;
|
2016-02-04 20:25:52 +01:00
|
|
|
vst.video_time_base = vst.fCtx->streams[i]->time_base;
|
2014-01-10 14:47:21 +01:00
|
|
|
if(stream_duration == 0)
|
2016-02-01 16:10:02 +01:00
|
|
|
stream_duration = vst.fCtx->streams[i]->duration;
|
2014-01-10 14:47:21 +01:00
|
|
|
}
|
2016-02-01 16:10:02 +01:00
|
|
|
|
|
|
|
if(vst.fCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
|
|
|
|
vst.aStream < 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
vst.aStream = i;
|
2014-01-10 14:47:21 +01:00
|
|
|
if(stream_duration == 0)
|
2016-02-01 16:10:02 +01:00
|
|
|
stream_duration = vst.fCtx->streams[i]->duration;
|
2014-01-10 14:47:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
if(vst.vStream==-1)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
printf("Video stream not detected\n\r");
|
|
|
|
return -1; // Didn't find a video stream
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-02-04 00:55:46 +01:00
|
|
|
INIT_LIST_HEAD(&vst.input_list);
|
|
|
|
INIT_LIST_HEAD(&vst.output_list);
|
|
|
|
mutex_init(&vst.q_video.lock);
|
|
|
|
mutex_init(&vst.q_audio.lock);
|
|
|
|
mutex_init(&vst.gpu_lock);
|
|
|
|
mutex_init(&vst.decoder_lock);
|
|
|
|
mutex_init(&vst.input_lock);
|
|
|
|
mutex_init(&vst.output_lock);
|
|
|
|
|
2016-02-05 20:36:09 +01:00
|
|
|
vst.vCtx = vst.fCtx->streams[vst.vStream]->codec;
|
|
|
|
vst.aCtx = vst.fCtx->streams[vst.aStream]->codec;
|
2016-02-04 20:25:52 +01:00
|
|
|
|
2016-11-03 11:00:13 +01:00
|
|
|
// __asm__ __volatile__("int3");
|
|
|
|
|
2016-02-05 20:36:09 +01:00
|
|
|
if(init_video_decoder(&vst) != 0 )
|
2016-02-01 20:27:17 +01:00
|
|
|
return -1;
|
2016-02-01 16:10:02 +01:00
|
|
|
|
2016-02-05 20:36:09 +01:00
|
|
|
vst.aCtx->request_channel_layout = AV_CH_LAYOUT_STEREO;
|
2016-02-01 16:10:02 +01:00
|
|
|
vst.aCodec = avcodec_find_decoder(vst.aCtx->codec_id);
|
|
|
|
if(vst.aCodec)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
if(avcodec_open2(vst.aCtx, vst.aCodec, NULL) >= 0 )
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
WAVEHEADER whdr;
|
|
|
|
int fmt;
|
|
|
|
int channels;
|
|
|
|
|
|
|
|
printf("audio stream rate %d channels %d format %d\n",
|
2016-02-01 16:10:02 +01:00
|
|
|
vst.aCtx->sample_rate, vst.aCtx->channels, vst.aCtx->sample_fmt );
|
2014-01-10 14:47:21 +01:00
|
|
|
whdr.riff_id = 0x46464952;
|
|
|
|
whdr.riff_format = 0x45564157;
|
|
|
|
whdr.wFormatTag = 0x01;
|
2016-02-01 16:10:02 +01:00
|
|
|
whdr.nSamplesPerSec = vst.aCtx->sample_rate;
|
2014-01-10 14:47:21 +01:00
|
|
|
whdr.nChannels = 2;
|
|
|
|
whdr.wBitsPerSample = 16;
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
sample_rate = vst.aCtx->sample_rate;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-03-03 03:14:18 +01:00
|
|
|
vst.snd_format = test_wav(&whdr);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-03-03 03:14:18 +01:00
|
|
|
if( init_audio(&vst) )
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
decoder_buffer = (uint8_t*)av_mallocz(192000*2+64);
|
|
|
|
if( decoder_buffer != NULL )
|
|
|
|
{
|
2015-07-23 12:13:34 +02:00
|
|
|
mutex_init(&astream.lock);
|
2014-01-10 14:47:21 +01:00
|
|
|
astream.count = 0;
|
|
|
|
astream.buffer = (char *)av_mallocz(192000*3);
|
|
|
|
if( astream.buffer != NULL )
|
2016-02-05 20:36:09 +01:00
|
|
|
vst.has_sound = 1;
|
2014-01-10 14:47:21 +01:00
|
|
|
else
|
|
|
|
av_free(decoder_buffer);
|
|
|
|
}
|
2016-02-05 20:36:09 +01:00
|
|
|
if( vst.has_sound == 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
printf("Not enough memory for audio buffers\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else printf("Cannot open audio codec\n\r");
|
|
|
|
}
|
|
|
|
else printf("Unsupported audio codec!\n");
|
|
|
|
|
2016-02-04 20:25:52 +01:00
|
|
|
mutex_lock(&vst.decoder_lock);
|
|
|
|
create_thread(video_thread, &vst, 1024*1024);
|
|
|
|
if(mutex_lock_timeout(&vst.decoder_lock, 3000) == 0)
|
|
|
|
return -1;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-02-04 00:55:46 +01:00
|
|
|
decoder(&vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
//__asm__ __volatile__("int3");
|
|
|
|
|
2015-07-23 12:13:34 +02:00
|
|
|
while( threads_running &
|
|
|
|
(AUDIO_THREAD | VIDEO_THREAD))
|
|
|
|
delay(1);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2015-07-23 12:13:34 +02:00
|
|
|
if(astream.lock.handle)
|
|
|
|
mutex_destroy(&astream.lock);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-11-03 11:00:13 +01:00
|
|
|
vst.decoder->fini(&vst);
|
|
|
|
avcodec_close(vst.vCtx);
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
mutex_destroy(&vst.q_video.lock);
|
|
|
|
mutex_destroy(&vst.q_audio.lock);
|
2016-02-04 00:55:46 +01:00
|
|
|
mutex_destroy(&vst.decoder_lock);
|
2016-02-05 20:36:09 +01:00
|
|
|
|
2014-01-10 14:47:21 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
static int load_frame(vst_t *vst)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
AVPacket packet;
|
|
|
|
int err;
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
err = av_read_frame(vst->fCtx, &packet);
|
2014-01-10 14:47:21 +01:00
|
|
|
if( err == 0)
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
if(packet.stream_index == vst->vStream)
|
|
|
|
put_packet(&vst->q_video, &packet);
|
|
|
|
else if( (packet.stream_index == vst->aStream) &&
|
2016-02-05 20:36:09 +01:00
|
|
|
(vst->has_sound != 0) )
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
put_packet(&vst->q_audio, &packet);
|
2016-02-05 20:36:09 +01:00
|
|
|
if(vst->audio_timer_valid == 0 &&
|
|
|
|
packet.pts != AV_NOPTS_VALUE )
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
2016-02-05 20:36:09 +01:00
|
|
|
vst->audio_timer_base = get_audio_base(vst) * packet.pts;
|
|
|
|
vst->audio_timer_valid = 1;
|
2014-01-10 14:47:21 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
else av_free_packet(&packet);
|
|
|
|
}
|
|
|
|
else if (err != AVERROR_EOF)
|
|
|
|
printf("av_read_frame: error %x\n", err);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
static int fill_queue(vst_t* vst)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
AVPacket packet;
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
while( (vst->q_video.size < 4*1024*1024) && !err )
|
|
|
|
err = load_frame(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
static void flush_all(vst_t* vst)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
AVPacket packet;
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
avcodec_flush_buffers(vst->vCtx);
|
|
|
|
avcodec_flush_buffers(vst->aCtx);
|
|
|
|
while( get_packet(&vst->q_video, &packet) != 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
av_free_packet(&packet);
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
while( get_packet(&vst->q_audio, &packet)!= 0)
|
2014-01-10 14:47:21 +01:00
|
|
|
av_free_packet(&packet);
|
|
|
|
|
2016-02-01 17:55:10 +01:00
|
|
|
flush_video(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
astream.count = 0;
|
|
|
|
};
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
void decoder(vst_t* vst)
|
2014-01-10 14:47:21 +01:00
|
|
|
{
|
|
|
|
int eof;
|
|
|
|
AVPacket packet;
|
|
|
|
int ret, vret, aret;
|
|
|
|
|
|
|
|
int64_t min_pos, max_pos;
|
|
|
|
|
2015-07-21 16:28:32 +02:00
|
|
|
// av_log_set_level(AV_LOG_DEBUG);
|
|
|
|
|
2014-01-10 14:47:21 +01:00
|
|
|
while( player_state != CLOSED )
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch(decoder_state)
|
|
|
|
{
|
|
|
|
case PREPARE:
|
2016-02-01 16:10:02 +01:00
|
|
|
eof = fill_queue(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
if( (vst->q_video.size < 4*1024*1024) &&
|
2014-01-10 14:47:21 +01:00
|
|
|
(eof == 0) )
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
eof = load_frame(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
}
|
2016-02-01 16:10:02 +01:00
|
|
|
decode_video(vst);
|
|
|
|
ret = decode_audio(vst->aCtx, &vst->q_audio);
|
2016-02-04 00:55:46 +01:00
|
|
|
}while(astream.count < resampler_size*2 && ret == 1);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
sound_state = PREPARE;
|
|
|
|
decoder_state = PLAY;
|
|
|
|
player_state = PLAY;
|
|
|
|
|
|
|
|
case PLAY:
|
2016-02-01 16:10:02 +01:00
|
|
|
if( (vst->q_video.size < 4*1024*1024) &&
|
2014-01-10 14:47:21 +01:00
|
|
|
(eof == 0) )
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
eof = load_frame(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
}
|
2016-02-01 16:10:02 +01:00
|
|
|
vret = decode_video(vst);
|
|
|
|
aret = decode_audio(vst->aCtx, &vst->q_audio);
|
2014-01-10 14:47:21 +01:00
|
|
|
ret = vret | aret;
|
|
|
|
|
|
|
|
if( eof && !ret)
|
|
|
|
{
|
|
|
|
decoder_state = STOP;
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
if( (vret & aret) == -1)
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
if( (vst->q_video.size < 4*1024*1024) &&
|
2014-01-10 14:47:21 +01:00
|
|
|
(eof == 0) )
|
|
|
|
{
|
2016-02-01 16:10:02 +01:00
|
|
|
eof = load_frame(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
yield();
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
delay(1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
yield();
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case STOP:
|
|
|
|
delay(1);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
case PLAY_2_STOP:
|
|
|
|
while(sound_state != STOP)
|
|
|
|
delay(1);
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
flush_all(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
if (vst->fCtx->start_time != AV_NOPTS_VALUE)
|
|
|
|
rewind_pos = vst->fCtx->start_time;
|
2014-01-10 14:47:21 +01:00
|
|
|
else
|
|
|
|
rewind_pos = 0;
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
ret = avformat_seek_file(vst->fCtx, -1, INT64_MIN,
|
2014-01-10 14:47:21 +01:00
|
|
|
rewind_pos, INT64_MAX, 0);
|
|
|
|
|
|
|
|
decoder_state = STOP;
|
2016-03-03 03:14:18 +01:00
|
|
|
continue;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
case REWIND:
|
|
|
|
while(sound_state != STOP)
|
|
|
|
yield();
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
flush_all(vst);
|
2014-01-10 14:47:21 +01:00
|
|
|
int opts = 0;
|
|
|
|
if(rewind_pos < 0)
|
|
|
|
{
|
|
|
|
rewind_pos = -rewind_pos;
|
|
|
|
opts = AVSEEK_FLAG_BACKWARD;
|
|
|
|
};
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
if (vst->fCtx->start_time != AV_NOPTS_VALUE)
|
|
|
|
rewind_pos += vst->fCtx->start_time;
|
2014-01-10 14:47:21 +01:00
|
|
|
|
|
|
|
// printf("rewind %8"PRId64"\n", rewind_pos);
|
|
|
|
min_pos = rewind_pos - 1000000;
|
|
|
|
max_pos = rewind_pos + 1000000;
|
|
|
|
|
2016-02-01 16:10:02 +01:00
|
|
|
ret = avformat_seek_file(vst->fCtx, -1, INT64_MIN,
|
2014-01-10 14:47:21 +01:00
|
|
|
rewind_pos, INT64_MAX, 0);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
printf("could not seek to position %f\n",
|
|
|
|
(double)rewind_pos / AV_TIME_BASE);
|
|
|
|
}
|
|
|
|
decoder_state = PREPARE;
|
2016-03-03 03:14:18 +01:00
|
|
|
continue;
|
2014-01-10 14:47:21 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
2015-07-21 16:28:32 +02:00
|
|
|
|