⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mpeg4.cpp

📁 jpeg and mpeg 编解码技术源代码
💻 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 _WINDOWS
#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 rate
static 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 (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;
    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 {
      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) {
      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 + -