📄 mpeg4.cpp
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com *//* * mpeg4.cpp - implementation with ISO reference codec */#include <stdio.h>#include <stdlib.h>#include <stdio.h>#include <stdlib.h>#include "codec_plugin.h"#ifdef _WIN32#include <windows.h>#include <mmsystem.h>#endif // __PC_COMPILER_#include <type/typeapi.h>#include <sys/mode.hpp>#include <sys/vopses.hpp>#include <tools/entropy/bitstrm.hpp>#include <sys/tps_enhcbuf.hpp>#include <sys/decoder/enhcbufdec.hpp>#include <sys/decoder/vopsedec.hpp>#include "mpeg4.h"#include <gnu/strcasestr.h>#ifndef __GLOBAL_VAR_#define __GLOBAL_VAR_#endif#include <sys/global.hpp>#include <mp4v2/mp4.h>#include <mp4av/mp4av.h>#define iso_message (iso->m_vft->log_msg)static const char *mp4iso = "mp4iso";// Convert a hex character to it's decimal value.static char tohex (char a){ if (isdigit(a)) return (a - '0'); return (tolower(a) - 'a' + 10);}// Parse the format config passed in. This is the vo vod header// that we need to get width/height/frame ratestatic int parse_vovod (iso_decode_t *iso, const char *vovod, int ascii, uint32_t len){ unsigned char buffer[255]; unsigned char *bufptr; if (ascii == 1) { const char *config = strcasestr(vovod, "config="); if (config == NULL) { return 0; } config += strlen("config="); const char *end; end = config; while (isxdigit(*end)) end++; if (config == end) { return 0; } // config string will run from config to end len = end - config; // make sure we have even number of digits to convert to binary if ((len % 2) == 1) return 0; unsigned char *write; write = buffer; // Convert the config= from ascii to binary for (uint32_t ix = 0; ix < len; ix++) { *write = 0; *write = (tohex(*config)) << 4; config++; *write += tohex(*config); config++; write++; } len /= 2; bufptr = (unsigned char *)&buffer[0]; } else { bufptr = (unsigned char *)vovod; } // Create a byte stream to take from our buffer. // Temporary set of our bytestream // Get the VOL header. If we fail, set the bytestream back int havevol = 0; do { try { iso->m_pvodec->SetUpBitstreamBuffer(bufptr, len); iso->m_pvodec->decodeVOLHead(); iso->m_pvodec->postVO_VOLHeadInit(iso->m_pvodec->getWidth(), iso->m_pvodec->getHeight(), &iso->m_bSpatialScalability); iso_message(LOG_DEBUG, mp4iso, "Found VOL in header"); iso->m_vft->video_configure(iso->m_ifptr, iso->m_pvodec->getWidth(), iso->m_pvodec->getHeight(), VIDEO_FORMAT_YUV); havevol = 1; } catch (int err) { iso_message(LOG_DEBUG, mp4iso, "Caught exception in VOL mem header search"); } uint32_t used; used = iso->m_pvodec->get_used_bytes(); bufptr += used; len -= used; } while (havevol == 0 && len > 0); // We've found the VO VOL header - that's good. // Reset the byte stream back to what it was, delete our temp stream //player_debug_message("Decoded vovod header correctly"); return havevol;}static codec_data_t *iso_create (const char *compressor, int type, int profile, format_list_t *media_fmt, video_info_t *vinfo, const uint8_t *userdata, uint32_t ud_size, video_vft_t *vft, void *ifptr){ iso_decode_t *iso; iso = (iso_decode_t *)malloc(sizeof(iso_decode_t)); if (iso == NULL) return NULL; memset(iso, 0, sizeof(*iso)); iso->m_vft = vft; iso->m_ifptr = ifptr; iso->m_main_short_video_header = FALSE; iso->m_pvodec = new CVideoObjectDecoder(); iso->m_decodeState = DECODE_STATE_VOL_SEARCH; if (media_fmt != NULL && media_fmt->fmt_param != NULL) { // See if we can decode a passed in vovod header if (parse_vovod(iso, media_fmt->fmt_param, 1, 0) == 1) { iso->m_decodeState = DECODE_STATE_WAIT_I; } } else if (userdata != NULL) { if (parse_vovod(iso, (const char *)userdata, 0, ud_size) == 1) { iso->m_decodeState = DECODE_STATE_WAIT_I; } } iso->m_vinfo = vinfo; iso->m_num_wait_i = 0; iso->m_num_wait_i_frames = 0; iso->m_total_frames = 0; return (codec_data_t *)iso;}void iso_clean_up (iso_decode_t *iso){ if (iso->m_ifile != NULL) { fclose(iso->m_ifile); iso->m_ifile = NULL; } if (iso->m_buffer != NULL) { free(iso->m_buffer); iso->m_buffer = NULL; } if (iso->m_fpos != NULL) { delete iso->m_fpos; iso->m_fpos = NULL; } if (iso->m_pvodec) { delete iso->m_pvodec; iso->m_pvodec = NULL; } free(iso);}static void iso_close (codec_data_t *ptr){ iso_decode_t *iso = (iso_decode_t *)ptr; iso_message(LOG_INFO, mp4iso, "MPEG4 codec results:"); iso_message(LOG_INFO, mp4iso, "total frames : %u", iso->m_total_frames); iso_message(LOG_INFO, mp4iso, "wait for I times: %u", iso->m_num_wait_i); iso_message(LOG_INFO, mp4iso, "wait I frames : %u", iso->m_num_wait_i_frames); iso_clean_up(iso);}static void iso_do_pause (codec_data_t *ptr){ iso_decode_t *iso = (iso_decode_t *)ptr; if (iso->m_decodeState != DECODE_STATE_VOL_SEARCH) iso->m_decodeState = DECODE_STATE_WAIT_I;}static int iso_frame_is_sync (codec_data_t *ptr, uint8_t *buffer, uint32_t buflen, void *userdata){ u_char vop_type; while (buflen > 3 && !(buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 1 && buffer[3] == MP4AV_MPEG4_VOP_START)) { buffer++; buflen--; } vop_type = MP4AV_Mpeg4GetVopType(buffer, buflen);#if 0 { iso_decode_t *iso = (iso_decode_t *)ptr; iso_message(LOG_DEBUG, "iso", "return from get vop is %c %d", vop_type, vop_type); }#endif if (vop_type == 'I') return 1; return 0;}static int iso_decode (codec_data_t *ptr, uint64_t ts, int from_rtp, int *sync_frame, uint8_t *buffer, uint32_t buflen, void *userdata){ Int iEof = 1; iso_decode_t *iso = (iso_decode_t *)ptr; if (buflen <= 4) return -1; iso->m_total_frames++; buffer[buflen] = 0; buffer[buflen + 1] = 0; buffer[buflen + 2] = 1; iso->m_pvodec->SetUpBitstreamBuffer((unsigned char *)buffer, buflen + 3); switch (iso->m_decodeState) { case DECODE_STATE_VOL_SEARCH: { uint32_t used = 0; if (buffer[0] == 0 && buffer[1] == 0 && (buffer[2] & 0xfc) == 0x80 && (buffer[3] & 0x03) == 0x02) { // we have the short header iso->m_short_header = 1; iso->m_pvodec->SetUpBitstreamBuffer((unsigned char *)buffer, buflen); iso->m_pvodec->video_plane_with_short_header(); iso->m_pvodec->postVO_VOLHeadInit(iso->m_pvodec->getWidth(), iso->m_pvodec->getHeight(), &iso->m_bSpatialScalability); iso_message(LOG_INFO, mp4iso, "Decoding using short headers"); iso->m_vft->video_configure(iso->m_ifptr, iso->m_pvodec->getWidth(), iso->m_pvodec->getHeight(), VIDEO_FORMAT_YUV); iso->m_decodeState = DECODE_STATE_NORMAL; try { iEof = iso->m_pvodec->h263_decode(FALSE); } catch (...) { iso_message(LOG_ERR, mp4iso, "Couldn't decode h263 in vol search"); } break; } while (used < buflen && iso->m_decodeState == DECODE_STATE_VOL_SEARCH) { try { iso->m_pvodec->SetUpBitstreamBuffer((unsigned char *)buffer + used, buflen - used); iso->m_pvodec->decodeVOLHead(); iso->m_pvodec->postVO_VOLHeadInit(iso->m_pvodec->getWidth(), iso->m_pvodec->getHeight(), &iso->m_bSpatialScalability); iso_message(LOG_INFO, mp4iso, "Found VOL"); iso->m_vft->video_configure(iso->m_ifptr, iso->m_pvodec->getWidth(), iso->m_pvodec->getHeight(), VIDEO_FORMAT_YUV); iso->m_decodeState = DECODE_STATE_WAIT_I; used += iso->m_pvodec->get_used_bytes(); } catch (int err) { iso_message(LOG_DEBUG, mp4iso, "Caught exception in VOL search %d", err); if (err == 1) used = buflen; else used += iso->m_pvodec->get_used_bytes(); } } if (iso->m_decodeState != DECODE_STATE_WAIT_I) { if (iso->m_vinfo != NULL) { iso->m_pvodec->FakeOutVOVOLHead(iso->m_vinfo->height, iso->m_vinfo->width, 30, &iso->m_bSpatialScalability); iso->m_vft->video_configure(iso->m_ifptr, iso->m_vinfo->width, iso->m_vinfo->height, VIDEO_FORMAT_YUV); iso->m_decodeState = DECODE_STATE_NORMAL; } return used; } // else fall through } case DECODE_STATE_WAIT_I: try { iEof = iso->m_pvodec->decode(NULL, TRUE); if (iEof == -1) { iso->m_num_wait_i_frames++; return(iso->m_pvodec->get_used_bytes()); } iso_message(LOG_DEBUG, mp4iso, "Back to normal decode"); iso->m_decodeState = DECODE_STATE_NORMAL; iso->m_bCachedRefFrame = FALSE; iso->m_bCachedRefFrameCoded = FALSE; iso->m_cached_valid = FALSE; iso->m_cached_time = 0; } catch (int err) { if (err != 1) iso_message(LOG_DEBUG, mp4iso, "ts "LLU",Caught exception in wait_i %d", ts, err); return (iso->m_pvodec->get_used_bytes()); //return (-1); } break; case DECODE_STATE_NORMAL: try { if (iso->m_short_header != 0) { iEof = iso->m_pvodec->h263_decode(TRUE); } else { iEof = iso->m_pvodec->decode(NULL, FALSE, FALSE); } } catch (int err) { // This is because sometimes, the encoder doesn't read all the bytes // it should out of the rtp packet. The rtp bytestream does a read // and determines that we're trying to read across bytestreams. // If we get this, we don't want to change anything - just fall up // to the decoder thread so it gives us a new timestamp. if (err == 1) { // throw from running past end of frame return -1; } iso_message(LOG_DEBUG, mp4iso, "Mpeg4 ncaught %d -> waiting for I", err); iso->m_decodeState = DECODE_STATE_WAIT_I; return (iso->m_pvodec->get_used_bytes()); } catch (...) { iso_message(LOG_DEBUG, mp4iso, "Mpeg4 ncaught -> waiting for I"); iso->m_decodeState = DECODE_STATE_WAIT_I; //return (-1); return (iso->m_pvodec->get_used_bytes()); } break; } /* * We've got a good frame. See if we need to display it */ const CVOPU8YUVBA *pvopcQuant = NULL; if (iso->m_pvodec->fSptUsage() == 1) { //player_debug_message("Sprite"); } uint64_t displaytime = 0; int cached_ts = 0; if (iEof == EOF) { if (iso->m_bCachedRefFrame) { iso->m_bCachedRefFrame = FALSE; if (iso->m_bCachedRefFrameCoded) { pvopcQuant = iso->m_pvodec->pvopcRefQLater(); displaytime = ts; } } } else { if (iso->m_pvodec->vopmd().vopPredType == BVOP) { if (iEof != FALSE) { pvopcQuant = iso->m_pvodec->pvopcReconCurr(); displaytime = ts; } } else { if (iso->m_bCachedRefFrame) { iso->m_bCachedRefFrame = FALSE; if (iso->m_bCachedRefFrameCoded) { pvopcQuant = iso->m_pvodec->pvopcRefQPrev(); if (from_rtp) { int old_was_valid = iso->m_cached_valid; displaytime = iso->m_cached_time; cached_ts = 1; // old time stamp wasn't valid - instead of calculating it // ourselves, just punt on it. if (old_was_valid == 0) { return (iEof == EOF ? -1 : 0); } } else { displaytime = ts; } } } iso->m_cached_time = ts; iso->m_cached_valid = TRUE; iso->m_bCachedRefFrame = TRUE; iso->m_bCachedRefFrameCoded = (iEof != FALSE); } } if (pvopcQuant != NULL) {#if 0 player_debug_message("frame rtp_ts "LLU" disp "LLU" cached %d", ts, displaytime, cached_ts);#endif /* * Get the information to the video sync structure */ const uint8_t *y, *u, *v; int pixelw_y, pixelw_uv; pixelw_y = pvopcQuant->getPlane(Y_PLANE)->where().width; pixelw_uv = pvopcQuant->getPlane(U_PLANE)->where().width; y = (const uint8_t *)pvopcQuant->getPlane(Y_PLANE)->pixels(0,0); u = (const uint8_t *)pvopcQuant->getPlane(U_PLANE)->pixels(0,0); v = (const uint8_t *)pvopcQuant->getPlane(V_PLANE)->pixels(0,0); iso->m_last_time = displaytime;#if 0 player_debug_message("Adding video at "LLU" %d", displaytime, iso->m_pvodec->vopmd().vopPredType);#endif iso->m_vft->video_have_frame(iso->m_ifptr, y, u, v, pixelw_y, pixelw_uv, displaytime); } else { iso_message(LOG_DEBUG, mp4iso, "decode but no frame %llu", ts); } return (iso->m_pvodec->get_used_bytes());}static int iso_skip_frame (codec_data_t *iso){#if 0 return (iso_decode(iso, ts, 0, NULL, buffer, buflen));#else return 0;#endif}static const char *iso_compressors[] = { "mp4 ", "mp4v", "divx", "dvx1", "div4", "mpeg4", NULL,};static int iso_codec_check (lib_message_func_t message, const char *compressor, int type, int profile, format_list_t *fptr, const uint8_t *userdata, uint32_t userdata_size){ if (compressor != NULL && (strcasecmp(compressor, "MP4 FILE") == 0)) { if (type == MP4_MPEG4_VIDEO_TYPE || type == MP4_H263_VIDEO_TYPE) { return 1; } return -1; } if (fptr != NULL) { // find format. If matches, call parse_fmtp_for_mpeg4, look at // profile level. if (fptr->rtpmap != NULL && fptr->rtpmap->encode_name != NULL) { if (strcasecmp(fptr->rtpmap->encode_name, "MP4V-ES") == 0) { return 1; } } return -1; } if (compressor != NULL) { const char **lptr = iso_compressors; while (*lptr != NULL) { if (strcasecmp(*lptr, compressor) == 0) { return 1; } lptr++; } } return -1;}VIDEO_CODEC_WITH_RAW_FILE_PLUGIN("MPEG4 ISO", iso_create, iso_do_pause, iso_decode, NULL, iso_close, iso_codec_check, iso_frame_is_sync, mpeg4_iso_file_check, divx_file_next_frame, divx_file_used_for_frame, divx_file_seek_to, iso_skip_frame, divx_file_eof);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -