/******************************************************************* MPEG-2 stream edit module *******************************************************************/ #include #include #include #include #include "bitstream.h" #include "vfapi.h" #define MPEG2EDIT_C #include "mpeg2edit.h" typedef struct { unsigned char *data; int length; void *next; void *prev; } LIST_ELEMENT; typedef struct { int length; LIST_ELEMENT *head; } ES_SEQUENCE_HEADER; typedef struct { int length; LIST_ELEMENT *head; } ES_GOP_HEADER; typedef struct { int length; LIST_ELEMENT *head; } ES_PICTURE; typedef struct { ES_PICTURE fst; ES_PICTURE snd; } ES_FRAME; typedef struct { LIST_ELEMENT *pack_header; LIST_ELEMENT *pes_packet; __int64 scr_length; } PS_PACK; typedef struct { PS_PACK *data; void *prev; void *next; } PS_PACK_LIST_ELEMENT; typedef struct { __int64 in; __int64 out; __int64 input_field; __int64 output_field; int stream_id; int offset; int sequence; int picture; int output; int level; int step; int temporal_reference; int padding; int rate; int scale; int fps; int broken_link; } PS_PROC_VIDEO_OPTION; static const int PS_WASTE_PACK = 0; static const int PS_OUTPUT_PACK = 1; static const int PS_HEADER_PACK = 2; static const int PS_HEADER_CHAIN_PACK = 4; static const int PS_LEVEL_SEQUENCE = 0; static const int PS_LEVEL_GOP = 1; static const int PS_LEVEL_PICTURE = 2; static const int PS_LEVEL_FIELD = 3; static const int PS_STEP_START = 0; static const int PS_STEP_SEEK = 1; static const int PS_STEP_OUTPUT = 2; static const int PS_STEP_END = 3; typedef struct { unsigned char *data; int length; } BLOCK; /* function prototypes */ int open_mpeg2edit(const char *path, MPEG2EDIT *mpeg2edit); static void dummy_edit(void *mpeg2edit, char *out_path, __int64 in, __int64 out); static void dummy_close(void *mpeg2edit); static HRESULT _stdcall dummy_callback(char *out_path, DWORD percent); static LIST_ELEMENT *new_list_element(LIST_ELEMENT *pos, int unit_size); static void fwrite_list(LIST_ELEMENT *head, FILE *out); static void clear_list(LIST_ELEMENT *head); static void padding_list(LIST_ELEMENT *head, int data); static unsigned char get_byte_list(LIST_ELEMENT *head, int offset); static void set_byte_list(LIST_ELEMENT *head, int offset, unsigned char byte); static int find_list(LIST_ELEMENT *head, int offset, unsigned char *pattern, int length); static int count_list_size(LIST_ELEMENT *head); static BLOCK *new_block(unsigned char *data, int length); static void delete_block(BLOCK *block); static int find_block(BLOCK *current, BLOCK *next, int offset, BLOCK *pattern); static unsigned char get_byte_block(BLOCK *current, BLOCK *next, int offset); static void set_byte_block(BLOCK *current, BLOCK *next, int offset, unsigned char data); typedef int (* judge_read_list_end)(int code); static LIST_ELEMENT *read_list(BITSTREAM *in, int unit_size, judge_read_list_end func); static int subtract_temporal_reference(int current, int previous); static int es_open(const char *path, MPEG2EDIT *mpeg2edit); static void es_edit(void *mpeg2edit, char *out_path, __int64 in, __int64 out); static void es_close(void *mpeg2edit); static int es_read_sequence_header(BITSTREAM *in, ES_SEQUENCE_HEADER *out); static int es_read_gop_header(BITSTREAM *in, ES_GOP_HEADER *out); static int es_read_picture(BITSTREAM *in, ES_PICTURE *out); static int es_read_frame(BITSTREAM *in, ES_FRAME *out); static int es_judge_read_sequence_header_end(int code); static int es_judge_read_gop_header_end(int code); static int es_judge_read_picture_end(int code); static void es_write_sequence_header(ES_SEQUENCE_HEADER *in, FILE *out); static void es_write_gop_header(ES_GOP_HEADER *in, FILE *out); static void es_write_picture(ES_PICTURE *in, FILE *out); static void es_write_frame(ES_FRAME *in, FILE *out); static void es_write_sequence_end_code(FILE *out); static void es_clear_sequence_header(ES_SEQUENCE_HEADER *seq); static void es_clear_gop_header(ES_GOP_HEADER *gop); static void es_clear_picture(ES_PICTURE *pic); static void es_clear_frame(ES_FRAME *frm); static void es_padding_picture(ES_PICTURE *pic); static void es_padding_frame(ES_FRAME *frm); static int es_get_fps(ES_SEQUENCE_HEADER *seq); static void es_set_timecode(ES_GOP_HEADER *gop, __int64 frame, int fps); static int es_get_broken_link(ES_GOP_HEADER *gop); static void es_set_broken_link(ES_GOP_HEADER *gop, int broken_link); static void es_set_closed_gop(ES_GOP_HEADER *gop); static int es_get_picture_temporal_reference(ES_PICTURE *pic); static void es_set_picture_temporal_reference(ES_PICTURE *pic, int temporal_reference); static int es_get_picture_coding_type_picture(ES_PICTURE *pic); static int es_get_picture_field_count(ES_PICTURE *pic); static int es_get_temporal_reference(ES_FRAME *frm); static void es_set_temporal_reference(ES_FRAME *frm, int temporal_reference); static int es_get_coding_type(ES_FRAME *frm); static int es_get_field_count(ES_FRAME *frm); static int ps_open(const char *path, MPEG2EDIT *mpeg2edit); static void ps_edit(void *mpeg2edit, char *out_path, __int64 in, __int64 out); static void ps_close(void *mpeg2edit); static void ps_write_program_end_code(FILE *out); static int ps_read_pack(BITSTREAM *in, PS_PACK *out); static void ps_write_pack(PS_PACK *in, FILE *out); static void ps_clear_pack(PS_PACK *pack); static int ps_find_system_header(PS_PACK *pack); static int ps_get_video_stream_id(PS_PACK *pack); static int ps_find_stream(PS_PACK *pack, int stream_id); static __int64 ps_get_system_clock_reference(PS_PACK *pack); static void ps_set_system_clock_reference(PS_PACK *pack, __int64 scr); static __int64 ps_get_pack_scr_length(PS_PACK *pack); static void ps_set_pack_scr_length(PS_PACK *pack, __int64 scr_length); static int ps_proc_video(PS_PACK *current, PS_PACK *next, PS_PROC_VIDEO_OPTION *opt); static int ps_judge_read_pack_header_end(int code); static LIST_ELEMENT *ps_read_pes_packet(BITSTREAM *in); static void ps_init_proc_video_option(PS_PROC_VIDEO_OPTION *opt, __int64 in, __int64 out); static void ps_set_stream_id_proc_video_option(PS_PROC_VIDEO_OPTION *opt, int stream_id); static int ps_get_current_percent(PS_PROC_VIDEO_OPTION *opt); static PS_PACK_LIST_ELEMENT *ps_add_pack_list(PS_PACK_LIST_ELEMENT *pos, PS_PACK *data); static void ps_write_pack_list(PS_PACK_LIST_ELEMENT *head, FILE *out); static void ps_clear_pack_list(PS_PACK_LIST_ELEMENT *head); static int ps_check_pack_chain(PS_PACK_LIST_ELEMENT *tail, PS_PACK *next); static __int64 ps_get_pack_list_scr_length(PS_PACK_LIST_ELEMENT *head); static void ps_set_pack_list_scr(PS_PACK_LIST_ELEMENT *head, __int64 scr); static void ps_set_clock_reference_pes_packet(LIST_ELEMENT *pes_packet, __int64 diff); static BLOCK *ps_join_stream(PS_PACK *pack, int stream_id); static void ps_update_stream(PS_PACK *pack, int stream_id, BLOCK *data); static BLOCK *ps_get_pes_packet_data(LIST_ELEMENT *pes_packet); static void ps_set_pes_packet_data(LIST_ELEMENT *pes_packet, BLOCK *data); static int ps_proc_video_picture(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt); static int ps_proc_video_sequence(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt); static int ps_proc_video_end(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt); static int ps_proc_video_gop(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt); static int ps_proc_video_other(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt); static int ps_proc_video_extension(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt); static int ps_check_fill_zero_video(PS_PROC_VIDEO_OPTION *opt); static void ps_fill_zero_video(BLOCK *b1, BLOCK *b2, int from, int to); /*-----------------------------------------------------------------*/ int open_mpeg2edit(const char *path, MPEG2EDIT *mpeg2edit) { int n; int code; BITSTREAM *bs; mpeg2edit->edit = dummy_edit; mpeg2edit->callback = dummy_callback; mpeg2edit->close = dummy_close; bs = bs_open(path); if(bs == NULL){ return 0; } n = 0; while(bs_next_packet_prefix(bs)){ code = bs_read_bits(bs, 32); switch(code){ case 0x000001b3: /* sequence_header_code */ bs_close(bs); return es_open(path, mpeg2edit); case 0x000001ba: /* pack_start_code */ bs_close(bs); return ps_open(path, mpeg2edit); } n += 1; if(n == 8){ break; } } bs_close(bs); return 0; } /*-----------------------------------------------------------------*/ static void dummy_edit(void *mpeg2edit, char *out_path, __int64 in, __int64 out) { MPEG2EDIT *p; p = (MPEG2EDIT *)mpeg2edit; if(p->callback){ p->callback(out_path, 100); } return; } static HRESULT _stdcall dummy_callback(char *path, DWORD percent) { return VF_OK; /* do nothing */ } static void dummy_close(void *mpeg2edit) { MPEG2EDIT *p; p = (MPEG2EDIT *)mpeg2edit; p->stream = NULL; p->edit = dummy_edit; p->close = dummy_close; } /*-----------------------------------------------------------------*/ static LIST_ELEMENT *new_list_element(LIST_ELEMENT *pos, int unit_size) { LIST_ELEMENT *element; element = (LIST_ELEMENT *)calloc(1, sizeof(LIST_ELEMENT)); if(element == NULL){ return NULL; } element->data = (unsigned char *)malloc(unit_size); if(element->data == NULL){ free(element); return NULL; } element->length = 0; element->prev = pos; if(pos != NULL){ element->next = pos->next; pos->next = element; } return element; } static void fwrite_list(LIST_ELEMENT *head, FILE *out) { if(out == NULL){ return; } while(head){ fwrite(head->data, 1, head->length, out); head = (LIST_ELEMENT *)head->next; } } static void clear_list(LIST_ELEMENT *head) { LIST_ELEMENT *current; LIST_ELEMENT *next; current = head; while(current){ free(current->data); next = (LIST_ELEMENT *)current->next; free(current); current = next; } } static void padding_list(LIST_ELEMENT *head, int data) { while(head){ memset(head->data, data, head->length); head = (LIST_ELEMENT *)head->next; } } static unsigned char get_byte_list(LIST_ELEMENT *head, int offset) { int n; n = 0; while(head){ if(offset - n < head->length){ return head->data[offset-n]; } n += head->length; head = (LIST_ELEMENT *)head->next; } return 0; } static void set_byte_list(LIST_ELEMENT *head, int offset, unsigned char byte) { int n; n = 0; while(head){ if(offset - n < head->length){ head->data[offset-n] = byte; return; } n += head->length; head = (LIST_ELEMENT *)head->next; } } static int find_list(LIST_ELEMENT *head, int offset, unsigned char *pattern, int length) { int r; int i,n; LIST_ELEMENT *next; next = (LIST_ELEMENT *)head->next; n = 0; r = offset; while(head){ if(r-n < head->length){ for(i=0;ilength){ if(head->data[r-n+i] == pattern[i]){ continue; }else{ break; } }else if(next == NULL){ return -1; }else{ if(next->data[r-n-head->length+i] == pattern[i]){ continue; }else{ break; } } } if(i == length){ return r; } r += 1; }else{ n += head->length; head = next; if(next != NULL){ next = (LIST_ELEMENT *)head->next; } } } return -1; } static int count_list_size(LIST_ELEMENT *head) { int r; r = 0; while(head){ r += head->length; head = (LIST_ELEMENT *)head->next; } return r; } /*-----------------------------------------------------------------*/ static LIST_ELEMENT *read_list(BITSTREAM *in, int unit_size, judge_read_list_end func) { int n,code; LIST_ELEMENT *r; LIST_ELEMENT *current; current = new_list_element(NULL, unit_size); if(current == NULL){ return NULL; } r = current; code = bs_read_bits(in, 32); do{ if( (current->length+4) > unit_size){ current = new_list_element(current, unit_size); if(current == NULL){ return 0; } } if( (code >> 8) == 1 ){ n = bs_read(in, current->data+current->length, 4); current->length += n; } n = bs_read_next_packet_prefix(in, current->data+current->length, unit_size-current->length); current->length += n; code = bs_read_bits(in, 32); }while(n && (!func(code))); return r; } /*-----------------------------------------------------------------*/ static BLOCK *new_block(unsigned char *data, int length) { BLOCK *r; r = (BLOCK *)malloc(sizeof(BLOCK)); if(r == NULL){ return NULL; } r->data = (unsigned char *)calloc(length, 1); if(r->data == NULL){ free(r); return NULL; } r->length = length; memcpy(r->data, data, length); return r; } static void delete_block(BLOCK *block) { if(block){ if(block->data){ free(block->data); block->data = 0; } block->length = 0; free(block); } } static int find_block(BLOCK *current, BLOCK *next, int offset, BLOCK *pattern) { int r; int i; r = offset; while(r < current->length){ for(i=0;ilength;i++){ if(r+ilength){ if(current->data[r+i] == pattern->data[i]){ continue; }else{ break; } }else if(next != NULL){ if(next->data[r-current->length+i] == pattern->data[i]){ continue; }else{ break; } }else{ return -1; } } if(i == pattern->length){ return r; } r += 1; } return -1; } static unsigned char get_byte_block(BLOCK *current, BLOCK *next, int offset) { if(current == NULL){ return 0; } if(offset < current->length){ return current->data[offset]; }else if( (next != NULL) && (offset-current->length < next->length) ){ return next->data[offset-current->length]; } return 0; } static void set_byte_block(BLOCK *current, BLOCK *next, int offset, unsigned char data) { if(current == NULL){ return; } if(offset < current->length){ current->data[offset] = data; }else if( (next != NULL) && (offset-current->length < next->length) ){ next->data[offset-current->length] = data; } } /*-----------------------------------------------------------------*/ static int subtract_temporal_reference(int current, int previous) { if( (previous & 0x0200) && ((current & 0x03fe) == 0) ){ current |= 0x0400; } return current - previous; } /*-----------------------------------------------------------------*/ static int es_open(const char *path, MPEG2EDIT *mpeg2edit) { BITSTREAM *bs; bs = bs_open(path); if(bs == NULL){ return 0; } if(! bs_next_packet_prefix(bs) ){ bs_close(bs); return 0; } if( bs_read_bits(bs, 32) != 0x000001b3 ){ bs_close(bs); return 0; } bs_seek(bs, 0, SEEK_SET); mpeg2edit->stream = bs; mpeg2edit->edit = es_edit; mpeg2edit->close = es_close; return 1; } static void es_edit(void *mpeg2edit, char *out_path, __int64 in, __int64 out) { int current_temporal_reference; int previous_temporal_reference; int current_percent; __int64 input_field_count; __int64 output_field_count; int broken_link; int fps; __int64 n; ES_SEQUENCE_HEADER seq; ES_GOP_HEADER gop; ES_FRAME frm; BITSTREAM *bs; FILE *file; bs = (BITSTREAM *)((MPEG2EDIT *)mpeg2edit)->stream; bs_seek(bs, 0, SEEK_SET); current_temporal_reference = 0; previous_temporal_reference = 0; input_field_count = 0; memset(&seq, 0, sizeof(seq)); memset(&gop, 0, sizeof(gop)); memset(&frm, 0, sizeof(frm)); /* 1st step - search stream head I picture */ while(bs_next_packet_prefix(bs)){ switch(bs_read_bits(bs, 32)){ case 0x000001b3: es_clear_sequence_header(&seq); es_read_sequence_header(bs, &seq); break; case 0x000001b8: es_clear_gop_header(&gop); es_read_gop_header(bs, &gop); current_temporal_reference = 0; previous_temporal_reference = 0; break; case 0x00000100: es_clear_frame(&frm); es_read_frame(bs, &frm); if(es_get_coding_type(&frm) == 1){ broken_link = 1; goto ES_EDIT_2ND_STEP; } default: bs_erase_bits(bs, 32); } } es_clear_sequence_header(&seq); es_clear_gop_header(&gop); es_clear_frame(&frm); return; ES_EDIT_2ND_STEP: /* search in point */ if( in == 0 ){ goto ES_EDIT_3RD_STEP; } previous_temporal_reference = es_get_temporal_reference(&frm); input_field_count += es_get_field_count(&frm); while(bs_next_packet_prefix(bs)){ current_percent = (int)(input_field_count * 50 / out); if(((MPEG2EDIT *)mpeg2edit)->callback(out_path, current_percent) != VF_OK){ es_clear_sequence_header(&seq); es_clear_gop_header(&gop); es_clear_frame(&frm); return; } switch(bs_read_bits(bs, 32)){ case 0x000001b3: es_clear_sequence_header(&seq); es_read_sequence_header(bs, &seq); break; case 0x000001b8: es_clear_gop_header(&gop); es_read_gop_header(bs, &gop); broken_link = es_get_broken_link(&gop); current_temporal_reference = 0; previous_temporal_reference = 0; break; case 0x00000100: es_clear_frame(&frm); es_read_frame(bs, &frm); switch(es_get_coding_type(&frm)){ case 1: if(broken_link == 1){ broken_link = 2; current_temporal_reference = 0; }else{ broken_link = 0; current_temporal_reference = es_get_temporal_reference(&frm); } n = (input_field_count+1)/2; n += subtract_temporal_reference(current_temporal_reference, previous_temporal_reference); if( n >= in ){ goto ES_EDIT_3RD_STEP; } previous_temporal_reference = es_get_temporal_reference(&frm); input_field_count += es_get_field_count(&frm); break; case 2: broken_link = 0; previous_temporal_reference = es_get_temporal_reference(&frm); input_field_count += es_get_field_count(&frm); break; case 3: if(broken_link == 0){ input_field_count += es_get_field_count(&frm); } } break; default: bs_erase_bits(bs, 32); } } es_clear_sequence_header(&seq); es_clear_gop_header(&gop); es_clear_frame(&frm); return; ES_EDIT_3RD_STEP: /* output data to file */ output_field_count = 0; fps = es_get_fps(&seq); es_set_timecode(&gop, 0, fps); es_set_broken_link(&gop, 0); file = fopen(out_path, "wb"); if(file == NULL){ es_clear_sequence_header(&seq); es_clear_gop_header(&gop); es_clear_frame(&frm); return; } es_write_sequence_header(&seq, file); es_write_gop_header(&gop, file); previous_temporal_reference = es_get_temporal_reference(&frm); es_set_temporal_reference(&frm, 0); n = es_get_field_count(&frm); input_field_count += n; output_field_count += n; es_write_frame(&frm, file); while(bs_next_packet_prefix(bs)){ current_percent = (int)(input_field_count * 50 / out); if(((MPEG2EDIT *)mpeg2edit)->callback(out_path, current_percent) != VF_OK){ es_clear_sequence_header(&seq); es_clear_gop_header(&gop); es_clear_frame(&frm); return; } switch(bs_read_bits(bs, 32)){ case 0x000001b3: if( (input_field_count+1)/2 >= out ){ goto ES_EDIT_4TH_STEP; } es_clear_sequence_header(&seq); es_read_sequence_header(bs, &seq); es_write_sequence_header(&seq, file); break; case 0x000001b8: if( (input_field_count+1)/2 >= out ){ goto ES_EDIT_4TH_STEP; } es_clear_gop_header(&gop); es_read_gop_header(bs, &gop); broken_link = es_get_broken_link(&gop); current_temporal_reference = 0; previous_temporal_reference = 0; es_set_broken_link(&gop, 0); es_set_timecode(&gop, (output_field_count+1)/2, fps); es_write_gop_header(&gop, file); break; case 0x00000100: es_clear_frame(&frm); es_read_frame(bs, &frm); switch(es_get_coding_type(&frm)){ case 1: if( (input_field_count+1)/2 >= out ){ goto ES_EDIT_4TH_STEP; } if(broken_link == 1){ broken_link = 2; previous_temporal_reference = es_get_temporal_reference(&frm); }else{ broken_link = 0; } current_temporal_reference = es_get_temporal_reference(&frm); current_temporal_reference -= previous_temporal_reference; es_set_temporal_reference(&frm, current_temporal_reference); es_write_frame(&frm, file); n = es_get_field_count(&frm); input_field_count += n; output_field_count += n; break; case 2: if( (input_field_count+1)/2 >= out ){ goto ES_EDIT_4TH_STEP; } broken_link = 0; current_temporal_reference = es_get_temporal_reference(&frm); current_temporal_reference -= previous_temporal_reference; es_set_temporal_reference(&frm, current_temporal_reference); es_write_frame(&frm, file); n = es_get_field_count(&frm); input_field_count += n; output_field_count += n; break; case 3: n = es_get_field_count(&frm); if(broken_link == 0){ input_field_count += n; } current_temporal_reference = es_get_temporal_reference(&frm); if(current_temporal_reference < previous_temporal_reference){ es_padding_frame(&frm); }else{ current_temporal_reference -= previous_temporal_reference; es_set_temporal_reference(&frm, current_temporal_reference); output_field_count += n; } es_write_frame(&frm, file); } break; default: bs_erase_bits(bs, 32); } } ES_EDIT_4TH_STEP: es_write_sequence_end_code(file); es_clear_sequence_header(&seq); es_clear_gop_header(&gop); es_clear_frame(&frm); fclose(file); ((MPEG2EDIT *)mpeg2edit)->callback(out_path, 100); return; } static void es_close(void *mpeg2edit) { MPEG2EDIT *p; BITSTREAM *bs; p = (MPEG2EDIT *)mpeg2edit; bs = (BITSTREAM *)p->stream; bs_close(bs); p->stream = NULL; p->edit = dummy_edit; p->close = dummy_close; } static int es_read_sequence_header(BITSTREAM *in, ES_SEQUENCE_HEADER *out) { if(bs_read_bits(in, 32) != 0x000001b3){ return 0; } out->head = read_list(in, 512, es_judge_read_sequence_header_end); out->length = count_list_size(out->head); return out->length; } static int es_read_gop_header(BITSTREAM *in, ES_GOP_HEADER *out) { if(bs_read_bits(in, 32) != 0x000001b8){ return 0; } out->head = read_list(in, 128, es_judge_read_gop_header_end); out->length = count_list_size(out->head); return out->length; } static int es_read_picture(BITSTREAM *in, ES_PICTURE *out) { if(bs_read_bits(in, 32) != 0x00000100){ return 0; } out->head = read_list(in, 8192, es_judge_read_picture_end); out->length = count_list_size(out->head); return out->length; } static int es_read_frame(BITSTREAM *in, ES_FRAME *out) { int r; r = es_read_picture(in, &(out->fst)); if(es_get_picture_field_count(&(out->fst)) == 1){ r += es_read_picture(in, &(out->snd)); } return r; } static int es_judge_read_sequence_header_end(int code) { if(code == 0x00000100){ /* picture_start_code */ return 1; }else if(code == 0x000001b8){ /* gop_start_code */ return 1; } return 0; } static int es_judge_read_gop_header_end(int code) { if(code == 0x00000100){ /* picture_start_code */ return 1; } return 0; } static int es_judge_read_picture_end(int code) { if(code == 0x00000100){ /* picture_start_code */ return 1; }else if(code == 0x000001b3){ /* sequence_start_code */ return 1; }else if(code == 0x000001b8){ /* gop_start_code */ return 1; }else if(code == 0x000001b7){ /* sequence_end_code */ return 1; } return 0; } static void es_write_sequence_header(ES_SEQUENCE_HEADER *in, FILE *out) { fwrite_list(in->head, out); } static void es_write_gop_header(ES_GOP_HEADER *in, FILE *out) { fwrite_list(in->head, out); } static void es_write_picture(ES_PICTURE *in, FILE *out) { fwrite_list(in->head, out); } static void es_write_frame(ES_FRAME *in, FILE *out) { es_write_picture(&(in->fst), out); es_write_picture(&(in->snd), out); } static void es_write_sequence_end_code(FILE *out) { unsigned char sequence_end_code[4] = { 0, 0, 1, 0xb7, }; fwrite(sequence_end_code, 1, 4, out); } static void es_clear_sequence_header(ES_SEQUENCE_HEADER *seq) { clear_list(seq->head); seq->head = NULL; seq->length = 0; } static void es_clear_gop_header(ES_GOP_HEADER *gop) { clear_list(gop->head); gop->head = NULL; gop->length = 0; } static void es_clear_picture(ES_PICTURE *pic) { clear_list(pic->head); pic->head = NULL; pic->length = 0; } static void es_clear_frame(ES_FRAME *frm) { es_clear_picture(&(frm->fst)); es_clear_picture(&(frm->snd)); } static void es_padding_picture(ES_PICTURE *pic) { padding_list(pic->head, 0); } static void es_padding_frame(ES_FRAME *frm) { es_padding_picture(&(frm->fst)); if(frm->snd.length){ es_padding_picture(&(frm->snd)); } } static int es_get_fps(ES_SEQUENCE_HEADER *seq) { int r; int rate; int scale; int offset; unsigned char code; unsigned char pattern[4] = { 0, 0, 1, 0xb5 }; static const int rate_table[16] = { 0, 23976, 24, 25, 2997, 30, 50, 5994, 60, 0, 0, 0, 0, 0, 0, 0, }; static const int scale_table[16] = { 1, 1000, 1, 1, 100, 1, 1, 100, 1, 1, 1, 1, 1, 1, 1, 1, }; code = get_byte_list(seq->head, 7); rate = rate_table[code & 0xf]; scale = scale_table[code & 0xf]; offset = find_list(seq->head, 12, pattern, 4); while(offset > 0){ if( (get_byte_list(seq->head, offset+4) & 0xf0) == 0x10){ code = get_byte_list(seq->head, offset+9); rate *= (((code >> 5) & 0x3) + 1); scale *= (((code >> 3) & 0x3) + 1); break; }else{ offset = find_list(seq->head, offset+4, pattern, 4); } } r = (rate+(scale-1))/scale; return r; } static void es_set_timecode(ES_GOP_HEADER *gop, __int64 frame, int fps) { int hh; int mm; int ss; int ff; unsigned char c0,c1,c2,c3; ff = (int)(frame % fps); frame /= fps; ss = (int)(frame % 60); frame /= 60; mm = (int)(frame % 60); frame /= 60; hh = (int)(frame % 24); c0 = (unsigned char)( (hh << 2) + (mm >> 4) ); c1 = (unsigned char)( ((mm & 0x0f) << 4) + 0x08 + (ss >> 3) ); c2 = (unsigned char)( ((ss & 7) << 5) + (ff >> 1) ); c3 = (unsigned char)( (get_byte_list(gop->head, 7) & 0x7f) + ((ff & 1) << 7) ); set_byte_list(gop->head, 4, c0); set_byte_list(gop->head, 5, c1); set_byte_list(gop->head, 6, c2); set_byte_list(gop->head, 7, c3); } static int es_get_broken_link(ES_GOP_HEADER *gop) { int r; r = (get_byte_list(gop->head, 7) & 0x20) >> 5; return r; } static void es_set_broken_link(ES_GOP_HEADER *gop, int broken_link) { unsigned char c; c = (unsigned char)(get_byte_list(gop->head, 7) & 0xdf); if(broken_link){ c |= 0x20; } set_byte_list(gop->head, 7, c); } static int es_get_picture_temporal_reference(ES_PICTURE *pic) { int r; r = get_byte_list(pic->head, 4) << 2; r += (get_byte_list(pic->head, 5) >> 6); return r; } static void es_set_picture_temporal_reference(ES_PICTURE *pic, int temporal_reference) { unsigned char c0; unsigned char c1; temporal_reference &= 0x000003ff; c0 = (unsigned char)(temporal_reference >> 2); c1 = (unsigned char)( (get_byte_list(pic->head, 5) & 0x3f) + ((temporal_reference & 3) << 6) ); set_byte_list(pic->head, 4, c0); set_byte_list(pic->head, 5, c1); } static int es_get_picture_coding_type(ES_PICTURE *pic) { int r; r = (get_byte_list(pic->head, 5) >> 3) & 3; return r; } static int es_get_picture_field_count(ES_PICTURE *pic) { int r; int offset; int code; unsigned char pattern[4] = { 0, 0, 1, 0xb5 }; r = 2; offset = find_list(pic->head, 8, pattern, 4); while(offset > 0){ if( (get_byte_list(pic->head, offset+4) & 0xf0) == 0x80){ code = get_byte_list(pic->head, offset+6); if( (code & 0x03) != 0x03 ){ return 1; } code = get_byte_list(pic->head, offset+7); if( (code & 0x02) == 0x02 ){ return 3; } break; }else{ offset = find_list(pic->head, offset+4, pattern, 4); } } return r; } static int es_get_temporal_reference(ES_FRAME *frm) { return es_get_picture_temporal_reference(&(frm->fst)); } static void es_set_temporal_reference(ES_FRAME *frm, int temporal_reference) { es_set_picture_temporal_reference(&(frm->fst), temporal_reference); if(frm->snd.length){ es_set_picture_temporal_reference(&(frm->snd), temporal_reference); } } static int es_get_coding_type(ES_FRAME *frm) { return es_get_picture_coding_type(&(frm->fst)); } static int es_get_field_count(ES_FRAME *frm) { int r; r = es_get_picture_field_count(&(frm->fst)); if(frm->snd.length){ r += es_get_picture_field_count(&(frm->snd)); } return r; } /*-----------------------------------------------------------------*/ static int ps_open(const char *path, MPEG2EDIT *mpeg2edit) { BITSTREAM *bs; bs = bs_open(path); if(bs == NULL){ return 0; } if(! bs_next_packet_prefix(bs) ){ bs_close(bs); return 0; } if( bs_read_bits(bs, 32) != 0x000001ba ){ /* pack_start_code */ bs_close(bs); return 0; } bs_seek(bs, 0, SEEK_SET); mpeg2edit->stream = bs; mpeg2edit->edit = ps_edit; mpeg2edit->close = ps_close; return 1; } static void ps_edit(void *mpeg2edit, char *out_path, __int64 in, __int64 out) { BITSTREAM *bs; FILE *file; PS_PACK_LIST_ELEMENT *header; PS_PACK_LIST_ELEMENT *header_tail; PS_PACK_LIST_ELEMENT *other; PS_PACK_LIST_ELEMENT *other_tail; PS_PACK *system; PS_PACK *video1; PS_PACK *video2; PS_PACK *previous; PS_PACK *current; PS_PROC_VIDEO_OPTION opt; int status; int stream_id; ps_init_proc_video_option(&opt, in, out); header = NULL; header_tail = NULL; other = NULL; other_tail = NULL; system = NULL; video1 = NULL; video2 = NULL; stream_id = 0; status = PS_WASTE_PACK; bs = ((MPEG2EDIT *)mpeg2edit)->stream; bs_seek(bs, 0, SEEK_SET); bs_next_packet_prefix(bs); current = (PS_PACK *)calloc(1, sizeof(PS_PACK)); previous = NULL; while(current){ if(((MPEG2EDIT *)mpeg2edit)->callback(out_path, ps_get_current_percent(&opt)) != VF_OK){ break; } ps_read_pack(bs, current); if(previous != NULL){ ps_set_pack_scr_length(previous, ps_get_system_clock_reference(current)-ps_get_system_clock_reference(previous)); } previous = current; if( (stream_id & 0xf0) != 0xe0 ){ stream_id = ps_get_video_stream_id(current); ps_set_stream_id_proc_video_option(&opt, stream_id); } if( ps_find_system_header(current) ){ if(system){ ps_clear_pack(system); free(system); } system = current; } if( (stream_id & 0xf0) == 0xe0 ){ if( ps_find_stream(current, stream_id) ){ video2 = current; if(video1){ status = ps_proc_video(video1, video2, &opt); if(status & PS_OUTPUT_PACK){ goto PS_EDIT_2ND_STEP; }else if(status & PS_HEADER_PACK){ ps_clear_pack_list(header); header = ps_add_pack_list(NULL, video1); header_tail = header; }else if(status & PS_HEADER_CHAIN_PACK){ header_tail = ps_add_pack_list(header_tail, video1); }else{ ps_clear_pack(video1); } } video1 = video2; } }else if(!ps_find_system_header(current)){ if(ps_check_pack_chain(other_tail, current)){ other_tail = ps_add_pack_list(other_tail, current); }else{ ps_clear_pack_list(other); other = ps_add_pack_list(NULL, current); other_tail = other; } } if(! bs_next_packet_prefix(bs) ){ break; } if( bs_read_bits(bs, 32) == 0x000001ba ){ current = (PS_PACK *)calloc(1, sizeof(PS_PACK)); }else{ current = NULL; } } ps_clear_pack_list(header); ps_clear_pack_list(other); ps_clear_pack(system); free(system); ps_clear_pack(video1); free(video1); return; PS_EDIT_2ND_STEP: file = fopen(out_path, "wb"); if(file == NULL){ ps_clear_pack_list(header); ps_clear_pack_list(other); ps_clear_pack(system); free(system); ps_clear_pack(video1); free(video1); return; } if(status & PS_HEADER_PACK){ if( (system != NULL) && ps_find_system_header(video1) ){ ps_clear_pack(system); free(system); system = NULL; } ps_clear_pack_list(header); header = NULL; header_tail = NULL; }else{ if( (system != NULL) && (header != NULL) && ps_find_system_header(header->data) ){ ps_clear_pack(system); free(system); system = NULL; } } current = video1; if(header){ ps_set_pack_list_scr(header, ps_get_system_clock_reference(current)-ps_get_pack_list_scr_length(header)); current = header->data; } if(system){ ps_set_system_clock_reference(system, ps_get_system_clock_reference(current)-ps_get_pack_scr_length(system)); ps_write_pack(system, file); ps_clear_pack(system); free(system); } ps_write_pack_list(header, file); ps_clear_pack_list(header); ps_write_pack(video1, file); ps_clear_pack(video1); free(video1); video1 = video2; ps_write_pack_list(other, file); if(! bs_next_packet_prefix(bs) ){ ps_clear_pack_list(other); ps_clear_pack(video1); free(video1); fclose(file); return; } if( bs_read_bits(bs, 32) == 0x000001ba ){ current = (PS_PACK *)calloc(1, sizeof(PS_PACK)); }else{ current = NULL; } while(current){ if(((MPEG2EDIT *)mpeg2edit)->callback(out_path, ps_get_current_percent(&opt)) != VF_OK){ ps_clear_pack_list(other); ps_clear_pack(video1); free(video1); fclose(file); return; } ps_read_pack(bs, current); ps_set_pack_scr_length(previous, ps_get_system_clock_reference(current)-ps_get_system_clock_reference(previous)); previous = current; if(ps_find_stream(current, stream_id)){ video2 = current; status = ps_proc_video(video1, video2, &opt); if(status & PS_OUTPUT_PACK){ ps_write_pack(video1, file); ps_clear_pack(video1); free(video1); ps_write_pack_list(other, file); ps_clear_pack_list(other); other = NULL; other_tail = NULL; }else{ goto PS_EDIT_3RD_STEP; } video1 = video2; }else{ if(ps_check_pack_chain(other_tail, current)){ other_tail = ps_add_pack_list(other_tail, current); }else{ ps_clear_pack_list(other); other = ps_add_pack_list(NULL, current); other_tail = other; } } if(! bs_next_packet_prefix(bs) ){ break; } if( bs_read_bits(bs, 32) == 0x000001ba ){ current = (PS_PACK *)calloc(1, sizeof(PS_PACK)); }else{ current = NULL; } } ps_proc_video(video1, NULL, &opt); ps_write_pack(video1, file); ps_write_pack_list(other, file); video2 = NULL; PS_EDIT_3RD_STEP: ps_clear_pack_list(other); ps_clear_pack(video1); free(video1); ps_clear_pack(video2); free(video2); ps_write_program_end_code(file); fclose(file); ((MPEG2EDIT *)mpeg2edit)->callback(out_path, 100); return; } static void ps_close(void *mpeg2edit) { MPEG2EDIT *p; BITSTREAM *bs; p = (MPEG2EDIT *)mpeg2edit; bs = (BITSTREAM *)p->stream; bs_close(bs); p->stream = NULL; p->edit = dummy_edit; p->close = dummy_close; } static void ps_write_program_end_code(FILE *out) { unsigned char pattern[] = { 0, 0, 1, 0xb9, }; fwrite(pattern, 1, 4, out); } static int ps_read_pack(BITSTREAM *in, PS_PACK *out) { int r; if(bs_read_bits(in, 32) != 0x000001ba){ /* pack_start_code */ return 0; } out->pack_header = read_list(in, 512, ps_judge_read_pack_header_end); out->pes_packet = ps_read_pes_packet(in); out->scr_length = 0; r = count_list_size(out->pack_header); r += count_list_size(out->pes_packet); return r; } static void ps_write_pack(PS_PACK *in, FILE *out) { if(in == NULL){ return; } fwrite_list(in->pack_header, out); fwrite_list(in->pes_packet, out); } static void ps_clear_pack(PS_PACK *pack) { if(pack == NULL){ return; } clear_list(pack->pack_header); pack->pack_header = NULL; clear_list(pack->pes_packet); pack->pes_packet = NULL; } static int ps_find_system_header(PS_PACK *pack) { unsigned char pattern[4] = { 0, 0, 1, 0xbb, }; if(pack == NULL){ return 0; } if(find_list(pack->pack_header, 0, pattern, 4) < 0){ return 0; } return 1; } static int ps_get_video_stream_id(PS_PACK *pack) { LIST_ELEMENT *current; if(pack == NULL){ return 0; } current = pack->pes_packet; while(current){ if( (current->data[3] & 0xf0) == 0xe0 ){ return current->data[3]; } current = (LIST_ELEMENT *)current->next; } return 0; } static int ps_find_stream(PS_PACK *pack, int stream_id) { LIST_ELEMENT *current; if(pack == NULL){ return 0; } current = pack->pes_packet; while(current){ if(current->data[3] == stream_id){ return 1; } current = (LIST_ELEMENT *)current->next; } return 0; } static __int64 ps_get_system_clock_reference(PS_PACK *pack) { __int64 base,ext; if(pack == NULL){ return 0; } base = (get_byte_list(pack->pack_header, 4) >> 3) & 7; base <<= 2; base += get_byte_list(pack->pack_header, 4) & 3; base <<= 8; base += get_byte_list(pack->pack_header, 5); base <<= 5; base += (get_byte_list(pack->pack_header, 6) >> 3) & 0x1f; base <<= 2; base += get_byte_list(pack->pack_header, 6) & 3; base <<= 8; base += get_byte_list(pack->pack_header, 7); base <<= 5; base += (get_byte_list(pack->pack_header, 8) >> 3) & 0x1f; ext = get_byte_list(pack->pack_header, 8) & 3; ext <<= 7; ext += get_byte_list(pack->pack_header, 9) >> 1; return (base * 300) + ext; } static void ps_set_system_clock_reference(PS_PACK *pack, __int64 scr) { __int64 diff; __int64 base; __int64 ext; unsigned char c4; unsigned char c5; unsigned char c6; unsigned char c7; unsigned char c8; unsigned char c9; LIST_ELEMENT *pes_packet; if(pack == NULL){ return; } diff = scr - ps_get_system_clock_reference(pack); base = scr / 300; ext = scr % 300; c4 = (unsigned char)(0x40 + (((base >> 30) & 7) << 3) + 4 + ((base >> 28) & 3)); c5 = (unsigned char)((base >> 20) & 0xff); c6 = (unsigned char)((((base >> 15) & 0x1f) << 3) + 4 + ((base >> 13) & 3)); c7 = (unsigned char)((base >> 5) & 0xff); c8 = (unsigned char)(((base & 0x1f) << 3) + 4 + ((ext >> 7) & 3)); c9 = (unsigned char)(((ext & 0x7f) << 1) + 1); set_byte_list(pack->pack_header, 4, c4); set_byte_list(pack->pack_header, 5, c5); set_byte_list(pack->pack_header, 6, c6); set_byte_list(pack->pack_header, 7, c7); set_byte_list(pack->pack_header, 8, c8); set_byte_list(pack->pack_header, 9, c9); pes_packet = pack->pes_packet; while(pes_packet){ ps_set_clock_reference_pes_packet(pes_packet, diff); pes_packet = (LIST_ELEMENT *)pes_packet->next; } } static __int64 ps_get_pack_scr_length(PS_PACK *pack) { if(pack == NULL){ return 0; } return pack->scr_length; } static void ps_set_pack_scr_length(PS_PACK *pack, __int64 scr_length) { if(pack == NULL){ return; } pack->scr_length = scr_length; } static int ps_proc_video(PS_PACK *current, PS_PACK *next, PS_PROC_VIDEO_OPTION *opt) { BLOCK *b1; BLOCK *b2; int old_offset; int new_offset; int code; int r; static unsigned char start_code[] = { 0, 0, 1, }; static BLOCK pattern = { start_code, 3, }; b1 = ps_join_stream(current, opt->stream_id); b2 = ps_join_stream(next, opt->stream_id); old_offset = opt->offset; if(opt->step == PS_STEP_END){ opt->output = PS_WASTE_PACK; } while( (new_offset = find_block(b1, b2, old_offset, &pattern)) >= 0 ){ if((new_offset+3) < b1->length){ code = b1->data[new_offset+3]; }else{ code = b2->data[new_offset+3-b2->length]; } switch(code){ case 0x00: /* picture start code */ old_offset = ps_proc_video_picture(b1, b2, old_offset, new_offset, opt); break; case 0xb3: /* sequence start code */ old_offset = ps_proc_video_sequence(b1, b2, old_offset, new_offset, opt); break; case 0xb5: old_offset = ps_proc_video_extension(b1, b2, old_offset, new_offset, opt); break; case 0xb7: /* sequence end code */ old_offset = ps_proc_video_end(b1, b2, old_offset, new_offset, opt); break; case 0xb8: /* gop start code */ old_offset = ps_proc_video_gop(b1, b2, old_offset, new_offset, opt); break; default: /* other start code */ old_offset = ps_proc_video_other(b1, b2, old_offset, new_offset+4, opt); } } if(old_offset < b1->length){ old_offset = ps_proc_video_other(b1, b2, old_offset, b1->length, opt); } opt->offset = old_offset - b1->length; r = PS_WASTE_PACK; if(opt->sequence & (PS_HEADER_PACK|PS_HEADER_CHAIN_PACK)){ r |= opt->sequence; if(opt->picture){ opt->sequence = PS_WASTE_PACK; }else{ opt->sequence = PS_HEADER_CHAIN_PACK; } } r |= opt->output; ps_update_stream(current, opt->stream_id, b1); ps_update_stream(next, opt->stream_id, b2); delete_block(b1); delete_block(b2); return r; } static int ps_judge_read_pack_header_end(int code) { if(code > 0x000001bb){ return 1; } return 0; } static LIST_ELEMENT *ps_read_pes_packet(BITSTREAM *in) { int n,code; int packet_length; LIST_ELEMENT *r; LIST_ELEMENT *current; const int max_packet_length = 65542; code = bs_read_bits(in, 32); if( ((code & 0xffffff00) != 0x00000100) || ((code & 0x000000ff) < 0xbc) ){ return NULL; } current = new_list_element(NULL, max_packet_length); if(current == NULL){ return NULL; } r = current; do{ while(current->length < 6){ n = bs_read(in, current->data+current->length, 6-current->length); current->length += n; } packet_length = (current->data[4] << 8) + current->data[5]; packet_length += 6; /* packet_prefix(3) + stream_id(1) + data_length(2) */ while(current->length < packet_length){ n = bs_read(in, current->data+current->length, packet_length-current->length); current->length += n; } code = bs_read_bits(in, 32); current->data = (unsigned char *)realloc(current->data, current->length); if( ((code & 0xffffff00) == 0x00000100) && ((code & 0x000000ff) > 0xbb) ){ current = new_list_element(current, max_packet_length); }else{ current = NULL; } }while(current); return r; } static void ps_init_proc_video_option(PS_PROC_VIDEO_OPTION *opt, __int64 in, __int64 out) { opt->in = in; opt->out = out; opt->input_field = 0; opt->output_field = 0; opt->stream_id = 0; opt->offset = 0; opt->sequence = PS_WASTE_PACK; opt->picture = 0; opt->output = PS_WASTE_PACK; opt->level = PS_LEVEL_SEQUENCE; opt->step = PS_STEP_START; opt->temporal_reference = 0; opt->padding = 0; opt->rate = 30; opt->scale = 1; opt->fps = 30; opt->broken_link = 0; } static void ps_set_stream_id_proc_video_option(PS_PROC_VIDEO_OPTION *opt, int stream_id) { if( (stream_id & 0xf0) == 0xe0 ){ opt->stream_id = stream_id; } } static int ps_get_current_percent(PS_PROC_VIDEO_OPTION *opt) { __int64 r; r = opt->input_field * 50 / opt->out; return (int)r; } static PS_PACK_LIST_ELEMENT *ps_add_pack_list(PS_PACK_LIST_ELEMENT *pos, PS_PACK *data) { PS_PACK_LIST_ELEMENT *r; r = (PS_PACK_LIST_ELEMENT *)calloc(1, sizeof(PS_PACK_LIST_ELEMENT)); r->data = data; r->prev = pos; if( pos != NULL ){ r->next = pos->next; pos->next = r; } return r; } static void ps_write_pack_list(PS_PACK_LIST_ELEMENT *head, FILE *out) { while(head){ ps_write_pack(head->data, out); head = (PS_PACK_LIST_ELEMENT *)head->next; } } static void ps_clear_pack_list(PS_PACK_LIST_ELEMENT *head) { PS_PACK_LIST_ELEMENT *p; while(head){ ps_clear_pack(head->data); free(head->data); p = (PS_PACK_LIST_ELEMENT *)head->next; free(head); head = p; } } static int ps_check_pack_chain(PS_PACK_LIST_ELEMENT *tail, PS_PACK *next) { __int64 scr; if(tail == NULL){ return 0; } scr = ps_get_system_clock_reference(tail->data) + ps_get_pack_scr_length(tail->data); if(scr == ps_get_system_clock_reference(next)){ return 1; } return 0; } static __int64 ps_get_pack_list_scr_length(PS_PACK_LIST_ELEMENT *head) { __int64 r; r = 0; while(head){ r += ps_get_pack_scr_length(head->data); head = (PS_PACK_LIST_ELEMENT *)head->next; } return r; } static void ps_set_pack_list_scr(PS_PACK_LIST_ELEMENT *head, __int64 scr) { while(head){ ps_set_system_clock_reference(head->data, scr); scr += ps_get_pack_scr_length(head->data); head = (PS_PACK_LIST_ELEMENT *)head->next; } } static void ps_set_clock_reference_pes_packet(LIST_ELEMENT *pes_packet, __int64 diff) { static const unsigned char id_table[] = { 0, 1, 0, 0, /* 0xbc - 0xbf */ 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0 - 0xc7 */ 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc8 - 0xcf */ 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0 - 0xd7 */ 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd8 - 0xdf */ 1, 1, 1, 1, 1, 1, 1, 1, /* 0xe0 - 0xe7 */ 1, 1, 1, 1, 1, 1, 1, 1, /* 0xe8 - 0xef */ 0, 0, 0, 1, 1, 1, 1, 1, /* 0xf0 - 0xf7 */ 0, 1, 0, 0, 0, 0, 0, 0, /* 0xf8 - 0xff */ }; int id; unsigned char *p; int offset; int pts_dts_flag; int escr_flag; __int64 base; __int64 ext; __int64 cr; if(pes_packet->length < 9){ return; } p = pes_packet->data; id = p[3]; if(id < 0xbc){ return; } if(!id_table[id-0xbc]){ return; } pts_dts_flag = p[7] >> 6; escr_flag = (p[7] >> 5) & 1; offset = 9; if(pts_dts_flag == 2){ /* pts only */ base = (p[offset+0] >> 1) & 7; base <<= 8; base += p[offset+1]; base <<= 7; base += p[offset+2] >> 1; base <<= 8; base += p[offset+3]; base <<= 7; base += p[offset+4] >> 1; base += (diff / 300); p[offset+0] = (unsigned char)((p[offset+0] & 0xf0) + ((base >> 30) << 1) + 1); p[offset+1] = (unsigned char)((base >> 22) & 0xff); p[offset+2] = (unsigned char)((((base >> 15) << 1) & 0xfe) + 1); p[offset+3] = (unsigned char)((base >> 7) & 0xff); p[offset+4] = (unsigned char)(((base << 1) & 0xfe) + 1); offset += 5; }else if(pts_dts_flag == 3){ /* pts and dts */ base = (p[offset+0] >> 1) & 7; base <<= 8; base += p[offset+1]; base <<= 7; base += p[offset+2] >> 1; base <<= 8; base += p[offset+3]; base <<= 7; base += p[offset+4] >> 1; base += (diff / 300); p[offset+0] = (unsigned char)((p[offset+0] & 0xf0) + ((base >> 30) << 1) + 1); p[offset+1] = (unsigned char)((base >> 22) & 0xff); p[offset+2] = (unsigned char)((((base >> 15) << 1) & 0xfe) + 1); p[offset+3] = (unsigned char)((base >> 7) & 0xff); p[offset+4] = (unsigned char)(((base << 1) & 0xfe) + 1); offset += 5; /* pts */ base = (p[offset+0] >> 1) & 7; base <<= 8; base += p[offset+1]; base <<= 7; base += p[offset+2] >> 1; base <<= 8; base += p[offset+3]; base <<= 7; base += p[offset+4] >> 1; base += (diff / 300); p[offset+0] = (unsigned char)((p[offset+0] & 0xf0) + ((base >> 30) << 1) + 1); p[offset+1] = (unsigned char)((base >> 22) & 0xff); p[offset+2] = (unsigned char)((((base >> 15) << 1) & 0xfe) + 1); p[offset+3] = (unsigned char)((base >> 7) & 0xff); p[offset+4] = (unsigned char)(((base << 1) & 0xfe) + 1); offset += 5; /* dts */ } if(escr_flag){ base = (p[offset+0] >> 3) & 7; base <<= 2; base += p[offset+0] & 3; base <<= 8; base += p[offset+1]; base <<= 5; base += (p[offset+2] >> 3) & 0x1f; base <<= 2; base += p[offset+2] & 3; base <<= 8; base += p[offset+3]; base <<= 5; base += (p[offset+4] >> 3) & 0x1f; ext = p[offset+4] & 3; ext <<= 7; ext += p[offset+5] >> 1; cr = base * 300 + ext; cr += diff; base = cr / 300; ext = cr % 300; p[offset+0] = (unsigned char)((p[offset+0] & 0xc0) + (((base >> 30) & 7) << 3) + 4 + ((base >> 28) & 3)); p[offset+1] = (unsigned char)((base >> 20) & 0xff); p[offset+2] = (unsigned char)((((base >> 15) & 0x1f) << 3) + 4 + ((base >> 13) & 3)); p[offset+3] = (unsigned char)((base >> 5) & 0xff); p[offset+4] = (unsigned char)(((base & 0x1f) << 3) + 4 + ((ext >> 7) & 3)); p[offset+5] = (unsigned char)(((ext & 0x7f) << 1) + 1); } } static BLOCK *ps_join_stream(PS_PACK *pack, int stream_id) { BLOCK *r; BLOCK *p; LIST_ELEMENT *packet; int space; if(pack == NULL){ return NULL; } r = (BLOCK *)malloc(sizeof(BLOCK)); if(r == NULL){ return NULL; } r->data = NULL; r->length = 0; space = 0; packet = pack->pes_packet; while(packet){ if(packet->data[3] == stream_id){ space += packet->length; } packet = (LIST_ELEMENT *)packet->next; } r->data = (unsigned char *)calloc(space, 1); if(r->data == NULL){ free(r); return NULL; } packet = pack->pes_packet; while(packet){ if(packet->data[3] == stream_id){ p = ps_get_pes_packet_data(packet); if(p == NULL){ delete_block(r); return NULL; } memcpy(r->data+r->length, p->data, p->length); r->length += p->length; delete_block(p); } packet = (LIST_ELEMENT *)packet->next; } return r; } static void ps_update_stream(PS_PACK *pack, int stream_id, BLOCK *data) { LIST_ELEMENT *packet; if( (pack == NULL) || (data == NULL) ){ return; } packet = pack->pes_packet; while(packet){ if(packet->data[3] == stream_id){ ps_set_pes_packet_data(packet, data); } packet = (LIST_ELEMENT *)packet->next; } } static BLOCK *ps_get_pes_packet_data(LIST_ELEMENT *pes_packet) { BLOCK *r; int offset; int length; unsigned char *p; if(pes_packet == NULL){ return NULL; } if(pes_packet->length < 9){ return NULL; } p = pes_packet->data; length = (p[4] << 8) + p[5] - p[8] - 3; offset = 3 + 1 + 2 + 3 + p[8]; /* packet_prefix, stream_id, packet_length, common header, additional header length */ r = new_block(p+offset, length); return r; } static void ps_set_pes_packet_data(LIST_ELEMENT *pes_packet, BLOCK *data) { int length; int offset; unsigned char *p; if(pes_packet == NULL){ return; } if(pes_packet->length < 9){ return; } p = pes_packet->data; offset = 3 + 1 + 2 + 3 + p[8]; length = pes_packet->length - offset; memcpy(p+offset, data->data, length); if(data->length - length){ memmove(data->data, data->data+length, data->length-length); data->length -= length; }else{ free(data->data); data->data = NULL; data->length = 0; } } static int ps_proc_video_picture(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt) { int code; if(ps_check_fill_zero_video(opt)){ ps_fill_zero_video(b1, b2, old_offset, new_offset); } if(opt->level == PS_LEVEL_FIELD){ return new_offset+2; }else{ opt->level = PS_LEVEL_PICTURE; code = (get_byte_block(b1, b2, new_offset+5) >> 3) & 3; opt->picture = code; opt->padding = 0; if(code == 1){ /* I picture */ if(opt->step == PS_STEP_START){ opt->step = PS_STEP_SEEK; opt->input_field = 2; code = get_byte_block(b1, b2, new_offset+4) << 2; code += (get_byte_block(b1, b2, new_offset+5) >> 6) & 3; opt->temporal_reference = code; opt->broken_link = 1; if(opt->in == 0){ opt->output = PS_OUTPUT_PACK; opt->output_field = 2; opt->step = PS_STEP_OUTPUT; set_byte_block(b1, b2, new_offset+4, 0); code = get_byte_block(b1, b2, new_offset+5) & 0x3f; set_byte_block(b1, b2, new_offset+5, (unsigned char)code); return new_offset+6; } return new_offset+2; }else if(opt->step == PS_STEP_SEEK){ code = get_byte_block(b1, b2, new_offset+4) << 2; code += (get_byte_block(b1, b2, new_offset+5) >> 6) & 3; if( (opt->input_field+subtract_temporal_reference(code, opt->temporal_reference)+1)/2 >= opt->in ){ opt->output = PS_OUTPUT_PACK; opt->input_field +=2; opt->output_field = 2; opt->step = PS_STEP_OUTPUT; opt->temporal_reference = code; set_byte_block(b1, b2, new_offset+4, 0); code = get_byte_block(b1, b2, new_offset+5) & 0x3f; set_byte_block(b1, b2, new_offset+5, (unsigned char)code); return new_offset+6; } opt->temporal_reference = code; opt->input_field += 2; return new_offset+2; }else if(opt->step == PS_STEP_OUTPUT){ if( (opt->input_field+1)/2 >= opt->out ){ opt->step = PS_STEP_END; set_byte_block(b1, b2, new_offset+3, 0xb7); return new_offset+4; } code = get_byte_block(b1, b2, new_offset+4) << 2; code += (get_byte_block(b1, b2, new_offset+5) >> 6) & 3; if(opt->broken_link == 1){ opt->broken_link = 2; opt->temporal_reference = code; }else{ opt->broken_link = 0; } code -= opt->temporal_reference; set_byte_block(b1, b2, new_offset+4, (unsigned char)((code >> 2) & 0xff)); code = ((code & 3) << 6) + get_byte_block(b1, b2, new_offset+5) & 0x3f; set_byte_block(b1, b2, new_offset+5, (unsigned char)code); opt->input_field += 2; opt->output_field += 2; return new_offset+6; }else{ return new_offset+2; } }else if(code == 2){/* P picture */ if(opt->step == PS_STEP_SEEK){ opt->broken_link = 0; code = get_byte_block(b1, b2, new_offset+4) << 2; code += (get_byte_block(b1, b2, new_offset+5) >> 6) & 3; opt->temporal_reference = code; opt->input_field += 2; return new_offset+2; }else if(opt->step == PS_STEP_OUTPUT){ if( (opt->input_field+1)/2 >= opt->out ){ opt->step = PS_STEP_END; set_byte_block(b1, b2, new_offset+3, 0xb7); return new_offset+4; } opt->broken_link = 0; code = get_byte_block(b1, b2, new_offset+4) << 2; code += (get_byte_block(b1, b2, new_offset+5) >> 6) & 3; code -= opt->temporal_reference; set_byte_block(b1, b2, new_offset+4, (unsigned char)((code >> 2) & 0xff)); code = ((code & 3) << 6) + get_byte_block(b1, b2, new_offset+5) & 0x3f; set_byte_block(b1, b2, new_offset+5, (unsigned char)code); opt->input_field += 2; opt->output_field += 2; return new_offset+6; }else{ return new_offset+2; } }else if(code == 3){/* B picture */ if(opt->step == PS_STEP_SEEK){ if(opt->broken_link == 0){ opt->input_field += 2; } return new_offset+2; }else if(opt->step == PS_STEP_OUTPUT){ if(opt->broken_link == 0){ opt->input_field += 2; } code = get_byte_block(b1, b2, new_offset+4) << 2; code += (get_byte_block(b1, b2, new_offset+5) >> 6) & 3; if(code < opt->temporal_reference){ opt->padding = 1; return new_offset+2; } code -= opt->temporal_reference; set_byte_block(b1, b2, new_offset+4, (unsigned char)((code >> 2) & 0xff)); code = ((code & 3) << 6) + get_byte_block(b1, b2, new_offset+5) & 0x3f; set_byte_block(b1, b2, new_offset+5, (unsigned char)code); opt->output_field += 2; return new_offset+6; }else{ return new_offset+2; } } return new_offset+2; } } static int ps_proc_video_sequence(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt) { int code; static const int rate_table[16] = { 0, 23976, 24, 25, 2997, 30, 50, 5994, 60, 0, 0, 0, 0, 0, 0, 0, }; static const int scale_table[16] = { 1, 1000, 1, 1, 100, 1, 1, 100, 1, 1, 1, 1, 1, 1, 1, 1, }; if(ps_check_fill_zero_video(opt)){ ps_fill_zero_video(b1, b2, old_offset, new_offset); } if(opt->step == PS_STEP_OUTPUT){ if( (opt->input_field+1)/2 >= opt->out ){ set_byte_block(b1, b2, new_offset+3, 0xb7); opt->step = PS_STEP_END; return new_offset+4; } } opt->sequence = PS_HEADER_PACK; opt->picture = 0; opt->level = PS_LEVEL_SEQUENCE; code = get_byte_block(b1, b2, new_offset+7); opt->rate = rate_table[code & 0xf]; opt->scale = scale_table[code & 0xf]; opt->fps = (opt->rate+opt->scale-1)/opt->scale; return new_offset+8; } static int ps_proc_video_end(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt) { if(ps_check_fill_zero_video(opt)){ ps_fill_zero_video(b1, b2, old_offset, new_offset); } opt->step = PS_STEP_END; opt->level = PS_LEVEL_SEQUENCE; return new_offset+4; } static int ps_proc_video_gop(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt) { int code; __int64 frame; int hh; int mm; int ss; int ff; if(ps_check_fill_zero_video(opt)){ ps_fill_zero_video(b1, b2, old_offset, new_offset); } opt->level = PS_LEVEL_GOP; if(opt->step == PS_STEP_OUTPUT){ if( (opt->input_field+1)/2 >= opt->out ){ set_byte_block(b1, b2, new_offset+3, 0xb7); opt->step = PS_STEP_END; return new_offset+4; } opt->broken_link = (get_byte_block(b1, b2, new_offset+7) & 0x20) >> 5; opt->temporal_reference = 0; }else{ opt->broken_link = (get_byte_block(b1, b2, new_offset+7) & 0x20) >> 5; opt->temporal_reference = 0; } frame = (opt->output_field+1)/2; ff = (int)(frame % opt->fps); frame /= opt->fps; ss = (int)(frame % 60); frame /= 60; mm = (int)(frame % 60); frame /= 60; hh = (int)(frame % 24); code = (hh << 2) + (mm >> 4); set_byte_block(b1, b2, new_offset+4, (unsigned char)code); code = ((mm & 0x0f) << 4) + 0x08 + (ss >> 3); set_byte_block(b1, b2, new_offset+5, (unsigned char)code); code = ((ss & 7) << 5) + (ff >> 1); set_byte_block(b1, b2, new_offset+6, (unsigned char)code); code = (get_byte_block(b1, b2, new_offset+7) & 0x5f) + ((ff & 1) << 7); set_byte_block(b1, b2, new_offset+7, (unsigned char)code); return new_offset+2; } static int ps_proc_video_other(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt) { if(ps_check_fill_zero_video(opt)){ ps_fill_zero_video(b1, b2, old_offset, new_offset); } return new_offset; } static int ps_proc_video_extension(BLOCK *b1, BLOCK *b2, int old_offset, int new_offset, PS_PROC_VIDEO_OPTION *opt) { int code; if(ps_check_fill_zero_video(opt)){ ps_fill_zero_video(b1, b2, old_offset, new_offset); } if(opt->level == PS_LEVEL_SEQUENCE){ code = get_byte_block(b1, b2, new_offset+4) & 0xf0; if(code == 0x10){ code = get_byte_block(b1, b2, new_offset+9); opt->rate *= (((code >> 5) & 0x3) + 1); opt->scale *= (((code >> 3) & 0x3) + 1); opt->fps = (opt->rate+opt->scale-1)/opt->scale; } return new_offset+2; }else if(opt->level == PS_LEVEL_PICTURE){ code = get_byte_block(b1, b2, new_offset+4) & 0xf0; if(code == 0x80){ code = get_byte_block(b1, b2, new_offset+6); if( (code & 0x03) != 0x03 ){ opt->level = PS_LEVEL_FIELD; return new_offset+2; } code = get_byte_block(b1, b2, new_offset+7); if( (code & 0x02) == 0x02 ){ if(opt->picture == 3){ if(opt->broken_link == 0){ opt->input_field += 1; }else if(opt->padding == 0){ opt->output_field += 1; } }else{ opt->input_field += 1; opt->output_field += 1; } } } return new_offset+2; }else if(opt->level == PS_LEVEL_FIELD){ code = get_byte_block(b1, b2, new_offset+4) & 0xf0; if(code == 0x80){ code = get_byte_block(b1, b2, new_offset+6); if( (code & 0x03) != 0x03 ){ opt->level = PS_LEVEL_PICTURE; return new_offset+2; } } return new_offset+2; }else{ return new_offset+2; } } static int ps_check_fill_zero_video(PS_PROC_VIDEO_OPTION *opt) { if(opt->step == PS_STEP_START){ if(opt->level == PS_LEVEL_SEQUENCE){ return 0; }else if(opt->level == PS_LEVEL_GOP){ return 0; } return 1; }else if(opt->step == PS_STEP_SEEK){ if(opt->level == PS_LEVEL_SEQUENCE){ return 0; }else if(opt->level == PS_LEVEL_GOP){ return 0; } return 1; }else if(opt->step == PS_STEP_OUTPUT){ if(opt->padding){ return 1; } return 0; }else if(opt->step == PS_STEP_END){ return 1; } return 1; } static void ps_fill_zero_video(BLOCK *b1, BLOCK *b2, int from, int to) { if(b1 == NULL){ return; } if(from < b1->length){ if(to < b1->length){ memset(b1->data+from, 0, to-from); return; } memset(b1->data+from, 0, b1->length-from); from = 0; to -= b1->length; } if(b2 == NULL){ return; } if(from < b2->length){ if(to < b2->length){ memset(b2->data, 0, to); return; } memset(b2->data, 0, b2->length); } }