From 6c53a4235e42130a0895b94e2c3e51b5001847da Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 5 Sep 2006 19:19:54 +0000 Subject: [PATCH] r862: Merge 2.1: Updates of the Quicktime library. This revision should match GIT::b293d9f3f9ecdaac520b7f3437b2a75ee35ebf7c. --- quicktime/Makefile.am | 1 + quicktime/funcprotos.h | 22 +++ quicktime/libdv.c | 1 + quicktime/mpeg4.c | 414 +++---------------------------------------------- quicktime/qtcache.c | 161 +++++++++++++++++++ quicktime/qtffmpeg.c | 242 +++++++++++++++++++++-------- quicktime/qth264.c | 1 + quicktime/qtprivate.h | 31 +++- quicktime/quicktime.c | 8 +- quicktime/recover.c | 2 +- 10 files changed, 421 insertions(+), 462 deletions(-) create mode 100644 quicktime/qtcache.c diff --git a/quicktime/Makefile.am b/quicktime/Makefile.am index f637a239..cea1f4cb 100644 --- a/quicktime/Makefile.am +++ b/quicktime/Makefile.am @@ -55,6 +55,7 @@ libquicktimehv_la_SOURCES = \ mp4a.c \ mvhd.c \ plugin.c \ + qtcache.c \ qtdv.c \ qtffmpeg.c \ qth264.c \ diff --git a/quicktime/funcprotos.h b/quicktime/funcprotos.h index d7db810e..40c17dc1 100644 --- a/quicktime/funcprotos.h +++ b/quicktime/funcprotos.h @@ -491,6 +491,28 @@ void quicktime_copy_vbr_float(quicktime_vbr_t *vbr, +// Frame caching for keyframed video. +quicktime_cache_t* quicktime_new_cache(); +void quicktime_delete_cache(quicktime_cache_t *ptr); +void quicktime_reset_cache(quicktime_cache_t *ptr); +void quicktime_put_frame(quicktime_cache_t *ptr, + int64_t frame_number, + unsigned char *y, + unsigned char *u, + unsigned char *v, + int y_size, + int u_size, + int v_size); +// Return 1 if the frame was found. +int quicktime_get_frame(quicktime_cache_t *ptr, + int64_t frame_number, + unsigned char **y, + unsigned char **u, + unsigned char **v); +int quicktime_has_frame(quicktime_cache_t *ptr, + int64_t frame_number); +int64_t quicktime_cache_usage(quicktime_cache_t *ptr); + diff --git a/quicktime/libdv.c b/quicktime/libdv.c index f57768a6..7449c572 100644 --- a/quicktime/libdv.c +++ b/quicktime/libdv.c @@ -34,6 +34,7 @@ dv_t* dv_new() } dv->decoder = dv_decoder_new(0, 0, 0); + dv_set_error_log (dv->decoder, 0); dv->decoder->quality = DV_QUALITY_BEST; dv->decoder->prev_frame_decoded = 0; dv->use_mmx = 1; diff --git a/quicktime/mpeg4.c b/quicktime/mpeg4.c index 3065d387..a8af9f22 100644 --- a/quicktime/mpeg4.c +++ b/quicktime/mpeg4.c @@ -10,6 +10,7 @@ #include "avcodec.h" #include "colormodels.h" #include "funcprotos.h" +#include "qtffmpeg.h" #include "quicktime.h" #include "workarounds.h" #include ENCORE_INCLUDE @@ -23,29 +24,14 @@ #include #define FRAME_RATE_BASE 10000 +#define FIELDS 2 typedef struct { -#define FIELDS 2 - int decode_initialized[FIELDS]; - - -// FFMpeg internals - AVCodec *decoder[FIELDS]; - AVCodecContext *decoder_context[FIELDS]; - AVFrame picture[FIELDS]; - - -// Decore internals -// DEC_PARAM dec_param[FIELDS]; -// int decode_handle[FIELDS]; - -// Last frame decoded - long last_frame[FIELDS]; - - int got_key[FIELDS]; +// Decoder side + quicktime_ffmpeg_t *decoder; @@ -55,6 +41,7 @@ typedef struct +// Encoding side int encode_initialized[FIELDS]; // Information for picking the right library routines. // ID out of avcodec.h for the codec used. @@ -66,6 +53,7 @@ typedef struct // FFMpeg internals AVCodec *encoder[FIELDS]; AVCodecContext *encoder_context[FIELDS]; + AVFrame picture[FIELDS]; // Encore internals @@ -548,390 +536,31 @@ static int writes_colormodel(quicktime_t *file, } -static int delete_decoder(quicktime_mpeg4_codec_t *codec, int field) -{ - if(codec->decode_initialized[field]) - { - pthread_mutex_lock(&ffmpeg_lock); - - avcodec_close(codec->decoder_context[field]); - free(codec->decoder_context[field]); - - pthread_mutex_unlock(&ffmpeg_lock); - codec->decode_initialized[field] = 0; - } -} - - -static int init_decode(quicktime_t *file, - quicktime_mpeg4_codec_t *codec, - quicktime_trak_t *trak, - int current_field, - int width_i, - int height_i) -{ - quicktime_stsd_table_t *stsd_table = &trak->mdia.minf.stbl.stsd.table[0]; - quicktime_esds_t *esds = &stsd_table->esds; - - if(!ffmpeg_initialized) - { - ffmpeg_initialized = 1; - avcodec_init(); - avcodec_register_all(); - } - - - codec->decoder[current_field] = avcodec_find_decoder(codec->ffmpeg_id); - if(!codec->decoder[current_field]) - { - printf("init_decode: avcodec_find_decoder returned NULL.\n"); - return 1; - } - - AVCodecContext *context = - codec->decoder_context[current_field] = - avcodec_alloc_context(); - context->width = width_i; - context->height = height_i; - - - if(esds->mpeg4_header && esds->mpeg4_header_size) - { - context->extradata = esds->mpeg4_header; - context->extradata_size = esds->mpeg4_header_size; - } - - if(file->cpus > 1) - { - avcodec_thread_init(context, file->cpus); - context->thread_count = file->cpus; - } - - if(avcodec_open(context, - codec->decoder[current_field]) < 0) - { - printf("init_decode: avcodec_open failed.\n"); - } - return 0; -} - - -static int decode_wrapper(quicktime_t *file, - quicktime_video_map_t *vtrack, - quicktime_mpeg4_codec_t *codec, - int frame_number, - int current_field, - int track, - int drop_it) -{ - - int got_picture = 0; - int result = 0; - int bytes = 0; - int header_bytes = 0; - char *compressor = vtrack->track->mdia.minf.stbl.stsd.table[0].format; - quicktime_trak_t *trak = vtrack->track; - quicktime_stsd_table_t *stsd_table = &trak->mdia.minf.stbl.stsd.table[0]; - int width = trak->tkhd.track_width; - int height = trak->tkhd.track_height; - int width_i; - int height_i; - - if(codec->ffmpeg_id == CODEC_ID_SVQ1) - { - width_i = quicktime_quantize32(width); - height_i = quicktime_quantize32(height); - } - else - { - width_i = quicktime_quantize16(width); - height_i = quicktime_quantize16(height); - } - - - quicktime_set_video_position(file, frame_number, track); - - bytes = quicktime_frame_size(file, frame_number, track); - if(frame_number == 0 && codec->ffmpeg_id == CODEC_ID_SVQ3 && stsd_table->extradata) - { - codec->decoder_context[current_field]->extradata_size = stsd_table->extradata_size; - codec->decoder_context[current_field]->extradata = stsd_table->extradata; - } else - if(frame_number == 0) - { - header_bytes = stsd_table->esds.mpeg4_header_size; - } - - if(!codec->work_buffer || codec->buffer_size < bytes + header_bytes) - { - if(codec->work_buffer) free(codec->work_buffer); - codec->buffer_size = bytes + header_bytes; - codec->work_buffer = calloc(1, codec->buffer_size + 100); - } - - // Special handling of SVQ3 codec (some others might be alike) - - if(header_bytes) - memcpy(codec->work_buffer, stsd_table->esds.mpeg4_header, header_bytes); - - if(!quicktime_read_data(file, - codec->work_buffer + header_bytes, - bytes)) - result = -1; - - - if(!result) - { -/* - * if(!codec->got_key[current_field]) - * { - * if(!quicktime_mpeg4_is_key(codec->work_buffer, bytes, compressor)) - * return -1; - * else - * { - * codec->got_key[current_field] = 1; - * } - * } - */ - - -// No way to determine if there was an error based on nonzero status. -// Need to test row pointers to determine if an error occurred. - if(drop_it) - codec->decoder_context[current_field]->skip_frame = AVDISCARD_NONREF; - else - codec->decoder_context[current_field]->skip_frame = AVDISCARD_DEFAULT; - result = avcodec_decode_video(codec->decoder_context[current_field], - &codec->picture[current_field], - &got_picture, - codec->work_buffer, - bytes + header_bytes); - - - - if(codec->picture[current_field].data[0]) - { - if(!codec->got_key[current_field]) - { - codec->got_key[current_field] = 1; - } - result = 0; - } - else - { -// ffmpeg can't recover if the first frame errored out, like in a direct copy -// sequence. -/* - * delete_decoder(codec, current_field); - * init_decode(codec, current_field, width_i, height_i); - */ - result = 1; - } - -#ifdef ARCH_X86 - asm("emms"); -#endif - } - - return result; -} static int decode(quicktime_t *file, unsigned char **row_pointers, int track) { - int i; - int result = 0; quicktime_video_map_t *vtrack = &(file->vtracks[track]); quicktime_trak_t *trak = vtrack->track; quicktime_mpeg4_codec_t *codec = ((quicktime_codec_t*)vtrack->codec)->priv; + quicktime_stsd_table_t *stsd_table = &trak->mdia.minf.stbl.stsd.table[0]; int width = trak->tkhd.track_width; int height = trak->tkhd.track_height; - int width_i; - int height_i; - int use_temp = 0; - int input_cmodel; - int current_field = vtrack->current_position % codec->total_fields; - unsigned char **input_rows; - int seeking_done = 0; - - if(codec->ffmpeg_id == CODEC_ID_SVQ1) - { - width_i = quicktime_quantize32(width); - height_i = quicktime_quantize32(height); - } - else - { - width_i = quicktime_quantize16(width); - height_i = quicktime_quantize16(height); - } - - pthread_mutex_lock(&ffmpeg_lock); - - - if(!codec->decode_initialized[current_field]) - { - int current_frame = vtrack->current_position; - init_decode(file, codec, trak, current_field, width_i, height_i); -// Must decode frame with stream header first but only the first frame in the -// field sequence has a stream header. - result = decode_wrapper(file, - vtrack, - codec, - current_field, - current_field, - track, - 0); -// Reset position because decode wrapper set it - quicktime_set_video_position(file, current_frame, track); - codec->decode_initialized[current_field] = 1; - } - -// Handle seeking - if(quicktime_has_keyframes(file, track) && - vtrack->current_position != codec->last_frame[current_field] + codec->total_fields) - { - int frame1, frame2 = vtrack->current_position, current_frame = frame2; - int do_i_frame = 1; - -// Get first keyframe of same field - do - { - frame1 = quicktime_get_keyframe_before(file, - current_frame--, - track); - }while(frame1 > 0 && (frame1 % codec->total_fields) != current_field); - -// Keyframe is before last decoded frame and current frame is after last decoded -// frame, so instead of rerendering from the last keyframe we can rerender from -// the last decoded frame. - if(frame1 < codec->last_frame[current_field] && - frame2 > codec->last_frame[current_field]) - { - frame1 = codec->last_frame[current_field] + codec->total_fields; - do_i_frame = 0; - } - - while(frame1 <= frame2) - { - result = decode_wrapper(file, - vtrack, - codec, - frame1, - current_field, - track, - (frame1 < frame2)); - - -// May need to do the first I frame twice. - if(do_i_frame) - { - result = decode_wrapper(file, - vtrack, - codec, - frame1, - current_field, - track, - 0); - do_i_frame = 0; - } - frame1 += codec->total_fields; - } - - vtrack->current_position = frame2; - seeking_done = 1; - } - - - if(!seeking_done) - { - result = decode_wrapper(file, - vtrack, - codec, - vtrack->current_position, - current_field, - track, - 0); - } - pthread_mutex_unlock(&ffmpeg_lock); - - - - codec->last_frame[current_field] = vtrack->current_position; - - - - - - - - - -// result = (result != 0); - switch(codec->decoder_context[current_field]->pix_fmt) - { - case PIX_FMT_YUV420P: - input_cmodel = BC_YUV420P; - break; - case PIX_FMT_YUV422: - input_cmodel = BC_YUV422; - break; - case PIX_FMT_YUV422P: - input_cmodel = BC_YUV422P; - break; - case PIX_FMT_YUV410P: - input_cmodel = BC_YUV9P; - break; - default: - fprintf(stderr, - "mpeg4 decode: unrecognized color model %d\n", - codec->decoder_context[current_field]->pix_fmt); - input_cmodel = BC_YUV420P; - break; - } - - - - - - if(codec->picture[current_field].data[0]) - { + int result = 0; - input_rows = - malloc(sizeof(unsigned char*) * - codec->decoder_context[current_field]->height); - - - for(i = 0; i < codec->decoder_context[current_field]->height; i++) - input_rows[i] = codec->picture[current_field].data[0] + - i * - codec->decoder_context[current_field]->width * - cmodel_calculate_pixelsize(input_cmodel); - - - cmodel_transfer(row_pointers, /* Leave NULL if non existent */ - input_rows, - row_pointers[0], /* Leave NULL if non existent */ - row_pointers[1], - row_pointers[2], - codec->picture[current_field].data[0], /* Leave NULL if non existent */ - codec->picture[current_field].data[1], - codec->picture[current_field].data[2], - file->in_x, /* Dimensions to capture from input frame */ - file->in_y, - file->in_w, - file->in_h, - 0, /* Dimensions to project on output frame */ - 0, - file->out_w, - file->out_h, - input_cmodel, - file->color_model, - 0, /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */ - codec->picture[current_field].linesize[0], /* For planar use the luma rowspan */ - width); - free(input_rows); - } + if(!codec->decoder) codec->decoder = quicktime_new_ffmpeg( + file->cpus, + codec->total_fields, + codec->ffmpeg_id, + width, + height, + stsd_table); + if(codec->decoder) result = quicktime_ffmpeg_decode( + codec->decoder, + file, + row_pointers, + track); return result; @@ -1451,13 +1080,12 @@ static int delete_codec(quicktime_video_map_t *vtrack) } pthread_mutex_unlock(&ffmpeg_lock); } - delete_decoder(codec, i); } if(codec->temp_frame) free(codec->temp_frame); if(codec->work_buffer) free(codec->work_buffer); - + if(codec->decoder) quicktime_delete_ffmpeg(codec->decoder); free(codec); return 0; diff --git a/quicktime/qtcache.c b/quicktime/qtcache.c new file mode 100644 index 00000000..0648d28a --- /dev/null +++ b/quicktime/qtcache.c @@ -0,0 +1,161 @@ +#include "funcprotos.h" +#include "qtprivate.h" +#include + + +quicktime_cache_t* quicktime_new_cache() +{ + quicktime_cache_t *result = calloc(1, sizeof(quicktime_cache_t)); + return result; +} + +void quicktime_delete_cache(quicktime_cache_t *ptr) +{ + if(ptr->frames) + { + int i; +//printf("quicktime_delete_cache 1\n"); + for(i = 0; i < ptr->allocation; i++) + { + quicktime_cacheframe_t *frame = &ptr->frames[i]; + if(frame->y) free(frame->y); + if(frame->u) free(frame->u); + if(frame->v) free(frame->v); + } + free(ptr->frames); + free(ptr); + } +} + +void quicktime_reset_cache(quicktime_cache_t *ptr) +{ + ptr->total = 0; +} + +void quicktime_put_frame(quicktime_cache_t *ptr, + int64_t frame_number, + unsigned char *y, + unsigned char *u, + unsigned char *v, + int y_size, + int u_size, + int v_size) +{ + quicktime_cacheframe_t *frame = 0; + int i; + +//printf("quicktime_put_frame 1\n"); +// Get existing frame + for(i = 0; i < ptr->total; i++) + { + if(ptr->frames[i].frame_number == frame_number) + { + frame = &ptr->frames[i]; + break; + } + } + + + if(!frame) + { + if(ptr->total >= ptr->allocation) + { + int new_allocation = ptr->allocation * 2; +//printf("quicktime_put_frame 10 %d\n", new_allocation); + if(!new_allocation) new_allocation = 32; + ptr->frames = realloc(ptr->frames, + sizeof(quicktime_cacheframe_t) * new_allocation); + bzero(ptr->frames + ptr->total, + sizeof(quicktime_cacheframe_t) * (new_allocation - ptr->allocation)); + ptr->allocation = new_allocation; +//printf("quicktime_put_frame 20 %p %d %d\n", ptr, ptr->allocation, ptr->total); + } + + frame = &ptr->frames[ptr->total]; +//printf("quicktime_put_frame 30 %d %p %p %p\n", ptr->total, frame->y, frame->u, frame->v); + ptr->total++; + +// Memcpy is a lot slower than just dropping the seeking frames. + if(y) + { + frame->y = realloc(frame->y, y_size); + frame->y_size = y_size; + memcpy(frame->y, y, y_size); + } + + if(u) + { + frame->u = realloc(frame->u, u_size); + frame->u_size = u_size; + memcpy(frame->u, u, u_size); + } + + if(v) + { + frame->v = realloc(frame->v, v_size); + frame->v_size = v_size; + memcpy(frame->v, v, v_size); + } + frame->frame_number = frame_number; + } +//printf("quicktime_put_frame 100\n"); +} + +int quicktime_get_frame(quicktime_cache_t *ptr, + int64_t frame_number, + unsigned char **y, + unsigned char **u, + unsigned char **v) +{ + int i; + + for(i = 0; i < ptr->total; i++) + { + quicktime_cacheframe_t *frame = &ptr->frames[i]; + if(frame->frame_number == frame_number) + { + + *y = frame->y; + *u = frame->u; + *v = frame->v; + return 1; + break; + } + } + + return 0; +} + +int quicktime_has_frame(quicktime_cache_t *ptr, + int64_t frame_number) +{ + int i; + + for(i = 0; i < ptr->total; i++) + { + quicktime_cacheframe_t *frame = &ptr->frames[i]; + if(frame->frame_number == frame_number) + { + return 1; + break; + } + } + + return 0; +} + +int64_t quicktime_cache_usage(quicktime_cache_t *ptr) +{ + int64_t result = 0; + int i; +//printf("quicktime_cache_usage %p %d %lld\n", ptr, ptr->allocation, result); + for(i = 0; i < ptr->allocation; i++) + { + quicktime_cacheframe_t *frame = &ptr->frames[i]; + result += frame->y_size + frame->u_size + frame->v_size; + } + return result; +} + + + diff --git a/quicktime/qtffmpeg.c b/quicktime/qtffmpeg.c index 2ca03c86..44c908fa 100644 --- a/quicktime/qtffmpeg.c +++ b/quicktime/qtffmpeg.c @@ -67,30 +67,37 @@ quicktime_ffmpeg_t* quicktime_new_ffmpeg(int cpus, quicktime_delete_ffmpeg(ptr); return 0; } - ptr->decoder_context[i] = avcodec_alloc_context(); - ptr->decoder_context[i]->width = w; - ptr->decoder_context[i]->height = h; + + AVCodecContext *context = ptr->decoder_context[i] = avcodec_alloc_context(); + static char fake_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + context->width = ptr->width_i; + context->height = ptr->height_i; +// context->width = w; +// context->height = h; + context->extradata = fake_data; + context->extradata_size = 0; if(esds->mpeg4_header && esds->mpeg4_header_size) { - ptr->decoder_context[i]->extradata = esds->mpeg4_header; - ptr->decoder_context[i]->extradata_size = esds->mpeg4_header_size; + context->extradata = esds->mpeg4_header; + context->extradata_size = esds->mpeg4_header_size; } if(avcc->data && avcc->data_size) { - ptr->decoder_context[i]->extradata = avcc->data; - ptr->decoder_context[i]->extradata_size = avcc->data_size; + context->extradata = avcc->data; + context->extradata_size = avcc->data_size; } if(cpus > 1) { - avcodec_thread_init(ptr->decoder_context[i], cpus); - ptr->decoder_context[i]->thread_count = cpus; + avcodec_thread_init(context, cpus); + context->thread_count = cpus; } - if(avcodec_open(ptr->decoder_context[i], + if(avcodec_open(context, ptr->decoder[i]) < 0) { printf("quicktime_new_ffmpeg: avcodec_open failed.\n"); quicktime_delete_ffmpeg(ptr); } + ptr->last_frame[i] = -1; } pthread_mutex_unlock(&ffmpeg_lock); @@ -204,7 +211,32 @@ static int decode_wrapper(quicktime_t *file, return result; } - +// Get amount chroma planes are downsampled from luma plane. +// Used for copying planes into cache. +static int get_chroma_factor(quicktime_ffmpeg_t *ffmpeg, int current_field) +{ + switch(ffmpeg->decoder_context[current_field]->pix_fmt) + { + case PIX_FMT_YUV420P: + return 4; + break; + case PIX_FMT_YUV422: + return 2; + break; + case PIX_FMT_YUV422P: + return 2; + break; + case PIX_FMT_YUV410P: + return 9; + break; + default: + fprintf(stderr, + "get_chroma_factor: unrecognized color model %d\n", + ffmpeg->decoder_context[current_field]->pix_fmt); + return 9; + break; + } +} int quicktime_ffmpeg_decode(quicktime_ffmpeg_t *ffmpeg, quicktime_t *file, @@ -219,50 +251,101 @@ int quicktime_ffmpeg_decode(quicktime_ffmpeg_t *ffmpeg, int seeking_done = 0; int i; - pthread_mutex_lock(&ffmpeg_lock); +// Try frame cache + result = quicktime_get_frame(vtrack->frame_cache, + vtrack->current_position, + &ffmpeg->picture[current_field].data[0], + &ffmpeg->picture[current_field].data[1], + &ffmpeg->picture[current_field].data[2]); -// Handle seeking - if(quicktime_has_keyframes(file, track) && - vtrack->current_position != ffmpeg->last_frame[current_field] + ffmpeg->fields) - { - int frame1; - int frame2 = vtrack->current_position; - int current_frame = frame2; - int do_i_frame = 1; -// Get first keyframe of same field - do - { - frame1 = quicktime_get_keyframe_before(file, - current_frame--, - track); - }while(frame1 > 0 && (frame1 % ffmpeg->fields) != current_field); - -// Keyframe is before last decoded frame and current frame is after last decoded -// frame, so instead of rerendering from the last keyframe we can rerender from -// the last decoded frame. - if(frame1 < ffmpeg->last_frame[current_field] && - frame2 > ffmpeg->last_frame[current_field]) - { - frame1 = ffmpeg->last_frame[current_field] + ffmpeg->fields; - do_i_frame = 0; - } +// Didn't get frame + if(!result) + { +// Codecs which work without locking: +// H264 +// MPEG-4 +// pthread_mutex_lock(&ffmpeg_lock); +//printf("quicktime_ffmpeg_decode 1 %d\n", ffmpeg->last_frame[current_field]); -//printf("quicktime_ffmpeg_decode 2 %d\n", ffmpeg->last_frame[current_field]); - while(frame1 <= frame2) + if(ffmpeg->last_frame[current_field] == -1 && + ffmpeg->ffmpeg_id != CODEC_ID_H264) { + int current_frame = vtrack->current_position; +// For certain codecs, +// must decode frame with stream header first but only the first frame in the +// field sequence has a stream header. result = decode_wrapper(file, vtrack, ffmpeg, - frame1, + current_field, current_field, track, - (frame1 < frame2)); + 0); +// Reset position because decode wrapper set it + quicktime_set_video_position(file, current_frame, track); + ffmpeg->last_frame[current_field] = current_field; + } -// May need to do the first I frame twice. - if(do_i_frame) +// Handle seeking +// Seeking requires keyframes + if(quicktime_has_keyframes(file, track) && +// Not next frame + vtrack->current_position != ffmpeg->last_frame[current_field] + ffmpeg->fields && +// Same frame requested twice + vtrack->current_position != ffmpeg->last_frame[current_field]) + { + int frame1; + int first_frame; + int frame2 = vtrack->current_position; + int current_frame = frame2; + int do_i_frame = 1; + +// If an interleaved codec, the opposite field would have been decoded in the previous +// seek. + if(!quicktime_has_frame(vtrack->frame_cache, vtrack->current_position + 1)) + quicktime_reset_cache(vtrack->frame_cache); + +// Get first keyframe of same field + frame1 = current_frame; + do + { + frame1 = quicktime_get_keyframe_before(file, + frame1 - 1, + track); + }while(frame1 > 0 && (frame1 % ffmpeg->fields) != current_field); +//printf("quicktime_ffmpeg_decode 1 %d\n", frame1); + +// For MPEG-4, get another keyframe before first keyframe. +// The Sanyo tends to glitch with only 1 keyframe. +// Not enough memory. + if( 0 /* frame1 > 0 && ffmpeg->ffmpeg_id == CODEC_ID_MPEG4 */) + { + do + { + frame1 = quicktime_get_keyframe_before(file, + frame1 - 1, + track); + }while(frame1 > 0 && (frame1 & ffmpeg->fields) != current_field); +//printf("quicktime_ffmpeg_decode 2 %d\n", frame1); + } + +// Keyframe is before last decoded frame and current frame is after last decoded +// frame, so instead of rerendering from the last keyframe we can rerender from +// the last decoded frame. + if(frame1 < ffmpeg->last_frame[current_field] && + frame2 > ffmpeg->last_frame[current_field]) + { + frame1 = ffmpeg->last_frame[current_field] + ffmpeg->fields; + do_i_frame = 0; + } + + first_frame = frame1; + +//printf("quicktime_ffmpeg_decode 2 %d\n", ffmpeg->last_frame[current_field]); + while(frame1 <= frame2) { result = decode_wrapper(file, vtrack, @@ -270,32 +353,67 @@ int quicktime_ffmpeg_decode(quicktime_ffmpeg_t *ffmpeg, frame1, current_field, track, - 0); - do_i_frame = 0; +// Don't drop if we want to cache it + 0 /* (frame1 < frame2) */); + + if(ffmpeg->picture[current_field].data[0] && +// FFmpeg seems to glitch out if we include the first frame. + frame1 > first_frame) + { + int y_size = ffmpeg->picture[current_field].linesize[0] * ffmpeg->height_i; + int u_size = y_size / get_chroma_factor(ffmpeg, current_field); + int v_size = y_size / get_chroma_factor(ffmpeg, current_field); + quicktime_put_frame(vtrack->frame_cache, + frame1, + ffmpeg->picture[current_field].data[0], + ffmpeg->picture[current_field].data[1], + ffmpeg->picture[current_field].data[2], + y_size, + u_size, + v_size); + } + +// For some codecs, +// may need to do the same frame twice if it is the first I frame. + if(do_i_frame) + { + result = decode_wrapper(file, + vtrack, + ffmpeg, + frame1, + current_field, + track, + 0); + do_i_frame = 0; + } + frame1 += ffmpeg->fields; } - frame1 += ffmpeg->fields; - } - vtrack->current_position = frame2; - seeking_done = 1; - } + vtrack->current_position = frame2; + seeking_done = 1; + } - if(!seeking_done) - { - result = decode_wrapper(file, - vtrack, - ffmpeg, - vtrack->current_position, - current_field, - track, - 0); - } +// Not decoded in seeking process + if(!seeking_done && +// Same frame not requested + vtrack->current_position != ffmpeg->last_frame[current_field]) + { + result = decode_wrapper(file, + vtrack, + ffmpeg, + vtrack->current_position, + current_field, + track, + 0); + } - pthread_mutex_unlock(&ffmpeg_lock); +// pthread_mutex_unlock(&ffmpeg_lock); - ffmpeg->last_frame[current_field] = vtrack->current_position; + ffmpeg->last_frame[current_field] = vtrack->current_position; + } +// Hopefully this setting will be left over if the cache was used. switch(ffmpeg->decoder_context[current_field]->pix_fmt) { case PIX_FMT_YUV420P: diff --git a/quicktime/qth264.c b/quicktime/qth264.c index af73c289..5422c5ae 100644 --- a/quicktime/qth264.c +++ b/quicktime/qth264.c @@ -89,6 +89,7 @@ static int delete_codec(quicktime_video_map_t *vtrack) if(codec->temp_frame) free(codec->temp_frame); if(codec->work_buffer) free(codec->work_buffer); + if(codec->decoder) quicktime_delete_ffmpeg(codec->decoder); free(codec); diff --git a/quicktime/qtprivate.h b/quicktime/qtprivate.h index 0b00cb15..c3eae216 100644 --- a/quicktime/qtprivate.h +++ b/quicktime/qtprivate.h @@ -5,7 +5,7 @@ /* Version used internally. You need to query it with the C functions */ #define QUICKTIME_MAJOR 2 -#define QUICKTIME_MINOR 1 +#define QUICKTIME_MINOR 2 #define QUICKTIME_RELEASE 0 @@ -630,13 +630,29 @@ typedef struct int have_hdrl; } quicktime_riff_t; +typedef struct +{ + unsigned char *y, *u, *v; + int y_size; + int u_size; + int v_size; + int64_t frame_number; +} quicktime_cacheframe_t; + +typedef struct +{ + quicktime_cacheframe_t *frames; + int total; + int allocation; +} quicktime_cache_t; + /* table of pointers to every track */ typedef struct { quicktime_trak_t *track; /* real quicktime track corresponding to this table */ int channels; /* number of audio channels in the track */ - long current_position; /* current sample in output file */ - long current_chunk; /* current chunk in output file */ + int64_t current_position; /* current sample in output file */ + int64_t current_chunk; /* current chunk in output file */ quicktime_vbr_t vbr; /* Stores for vbr codecs */ void *codec; @@ -645,8 +661,10 @@ typedef struct typedef struct { quicktime_trak_t *track; - long current_position; /* current frame in output file */ - long current_chunk; /* current chunk in output file */ + int64_t current_position; /* current frame in output file */ + int64_t current_chunk; /* current chunk in output file */ +// Cache for the current GOP after a seek. + quicktime_cache_t *frame_cache; void *codec; } quicktime_video_map_t; @@ -799,6 +817,9 @@ typedef struct /* English description of codec. Optional. */ char *desc; +/* Frame cache for seeking only. */ + + /* Proprietary data for the codec to allocate and delete. */ void *priv; } quicktime_codec_t; diff --git a/quicktime/quicktime.c b/quicktime/quicktime.c index d52b287a..35782251 100644 --- a/quicktime/quicktime.c +++ b/quicktime/quicktime.c @@ -975,6 +975,7 @@ int quicktime_init_video_map(quicktime_video_map_t *vtrack, quicktime_trak_t *tr vtrack->current_position = 0; vtrack->current_chunk = 1; quicktime_init_vcodec(vtrack); + vtrack->frame_cache = quicktime_new_cache(); return 0; } @@ -982,6 +983,8 @@ int quicktime_delete_video_map(quicktime_video_map_t *vtrack) { int i; quicktime_delete_vcodec(vtrack); + if(vtrack->frame_cache) quicktime_delete_cache(vtrack->frame_cache); + vtrack->frame_cache = 0; return 0; } @@ -990,7 +993,10 @@ int64_t quicktime_memory_usage(quicktime_t *file) int i; int64_t result = 0; //printf("quicktime_memory_usage %d\n", file->total_vtracks); -// 2.0 to 2.1 patch placeholder + for(i = 0; i < file->total_vtracks; i++) + { + result += quicktime_cache_usage(file->vtracks[i].frame_cache); + } return result; } diff --git a/quicktime/recover.c b/quicktime/recover.c index 1931743b..84aba724 100644 --- a/quicktime/recover.c +++ b/quicktime/recover.c @@ -45,7 +45,7 @@ with the BUZ driver with PCM audio. #define FRAMERATE (double)30000/1001 #define CHANNELS 2 #define SAMPLERATE 48000 -#define BITS 16 +#define BITS 24 #define TEMP_FILE "/tmp/temp.mov" #define VCODEC QUICKTIME_MJPA //#define VCODEC QUICKTIME_JPEG -- 2.11.4.GIT