精华内容
下载资源
问答
  • MPEG4中文标准.pdf

    2018-03-20 16:45:14
    ASIC-MP4V_VID ——MPEG4 ASP 标准理解 。对 MPEG4 标准中的 Advanced Simple Profile(ASP)做一个完整的说明。
  • 为您提供佳佳MPEG4格式转换器下载,很多情况下需要对视频格式转换才能正常观看,佳佳MPEG4格式转换器是专业的MPEG4、H264和MP4格式转换工具,软件支持将MPEG4视频转换成多种格式音频播放,操作简单,转换速度快。...
  • mpeg4实现的软件代码 软件 mpeg-4 tts.part1.rar 1.91 MB, 下载次数: 65 , 下载积分: 资产 -2 信元, 下载支出 2 信元 mpeg-4 tts.part2.rar 1.91 MB, 下载次数: 56 , 下载积分: 资产 -2 信元, 下载支出 2 ...
  • mpeg4源代码库

    2019-09-04 10:24:31
    里面是MPEG4的源代码及说明,应该还有测试程序.是按照功能模块划分的,由VC++编写. 测试 , 程序 , 源代码
  • MPEG4Extractor的主要功能即是把Movie Box(moov)的信息解析出来,以便在播放的时候能够根据这些信息找到正确的媒体数据。比较重要的数据结构是Track(对应于trak Box)和它包含的SampleTable(对应于stbl Box和其子Box:...
  • mpeg4编码库源代码,C++完整源代码,有需要饿同学尽管拿去。 // This is the header file describing // the entrance function of the encoder core // or the encore ... #ifdef __cplusplus extern "C" { #...
  • MPEG全称是Moving Pictures Experts ...的英文缩写,该专家组成立于1988年,致力于运动图像及其伴音的压缩编码标准化工作,原先他们打算开发MPEG1、MPEG2、MPEG3和MPEG4四个版本,以适用于不同带宽和数字影像质量的要求
  • MPEG是Moving Pictures Experts Group(动态图象专家组)的简称,是国际标准化组织(ISO)成立的专责制定有关运动图像压缩编码标准的工作...MPEG2是DVD/超级VCD的视频图像压缩标准,MPEG4是网络视频图像压缩标准之一。
  • Mpeg4编解码源代码

    2019-09-01 12:55:23
    Mpeg4编解码源代码 源代码
  • MPEG2制定于1994年,是建立在MPEG1之上,设计目标是高级工业标准的图像质量以及更高的传输率。它主要应用在没有色度畸变要求场合的高质量视频,数据速率在1.1Mbps到20Mbps之间。MPEG2能够提供广播级的视像和CD级的...
  • MPEG4(ASP)文件

    2018-05-18 16:28:48
    MPEG4(ASP)文件,MPEG4(ASP)Constant_bitrate,分辨率:720x576,音频编码:AAC
  • 吐血共享:mpeg4编解码代码大全
  • MPEG4IP的最新源码(2017年更新).rar MPEG4IP的最新源码(2017年更新).rar MPEG4IP的最新源码(2017年更新).rar MPEG4IP的最新源码(2017年更新).rar
  • Advanced Video Compressor 是世界领先的压缩MPEG4数字视频文件(MP4)的FLV, 3GP, AVI和其他流行视频格式的软件。先进的视频压缩( AVC )执行所有的视频处理任务,你需要得到您的视频文件为共享或存储,便携设备...
  • 本代码是mpeg4视频流文件转成mp4文件的demo.
  • Mpeg4编码播放器,能够实现Mpeg4格式的视频文件
  • (frameworks/av/media/libstagefright/MPEG4Writer.cpp) 一、MPEG4Writer相关流程 MPEG4Writer是Android stagefright媒体框架下一个的封装类,录制视频调用的MediaRecorder接口类的底层封装实现通过它完成。 以...

    (frameworks/av/media/libstagefright/MPEG4Writer.cpp)

    一、MPEG4Writer相关流程

    MPEG4Writer是Android stagefright媒体框架下一个的封装类,录制视频调用的MediaRecorder接口类的底层封装实现通过它完成。

    以视频为例,MPEG4Writer是视频录制的最后一环。

    图 1 MediaRecorder调用流程

    MPEG4Writer遵守 ISO 14496-12标准进行封装,MP4、3gp、ismv等我们常见的媒体封装格式都是以这种基础文件格式为基础衍生的。

    Android系统录像封装流程主要有三个步骤:

    1) 录制开始时,写入文件头部。

    2) 录制进行时,实时写入音视频轨迹的数据块。

    3) 录制结束时,写入索引信息并更新头部参数。

     

    索引负责描述音视频轨迹的特征,会随着音视频轨迹的存储而变化,所以通常做法会将录像文件索引信息放在音视频轨迹流后面,在媒体流数据写完(录像结束)后才能写入。可以看到,存放音视频数据的mdat box是位于第二位的,而负责检索音视频的moov box是位于最后的,这与通常的MP4封装的排列顺序不同,当然这是为了符合录制而产生的结果。因为 moov的大小是随着 mdat 变化的,而我们录制视频的时间预先是不知道的,所以需要先将mdat 数据写入,最后再写入moov,完成封装。

    现有Android系统上录像都是录制是MP4或3GP格式,底层就是使用MPEG4Writer组合器类来完成的,它将编码后的音视频轨迹按照MPEG4规范进行封装,填入各个参数,就组合成完整的MP4格式文件。MPEG4Writer的组合功能主要由两种线程完成,一种是负责音视频数据写入封装文件写线程(WriterThread,一种是音视频数据读取处理轨迹线程(TrackThread。轨迹线程一般有两个:视频轨迹数据读取线程和音频轨迹数据读取线程,而写线程只有一个,负责将轨迹线程中打包成Chunk的数据写入封装文件。

    如图2所示,轨迹线程是以帧为单位获取数据帧(Sample),并将每帧中的信息及系统环境信息提取汇总存储在内存的trak表中,其中需要维持的信息有Chunk写入文件的偏移地址Stco(Chunk Offset)、Sample与Chunk的映射关系Stsc(Sample-to-Chunk)、关键帧Stss(Sync Sample)、每一帧的持续时间Stts(Time-to-Sample)等,这些信息是跟每一帧的信息密切相关的,由图可以看出trak表由各自的线程维护,当录像结束时trak表会就会写入封装文件。而每一帧的数据流会先存入一个链表缓存中,当帧的数量达到一定值时,轨迹线程会将这些帧数据打包成块(Chunk)并通知写线程写入到封装文件。写线程接到Chunk已准备好的通知后就马上搜索Chunk链表(链表个数与轨迹线程个数相关,一般有两个,音视频轨迹线程各有一个),将找到的第一个Chunk后便写入封装文件,并会将写入的偏移地址更新到相应的trak表的Stco项(但trak表中其它数据是由轨迹线程更新)。音视频的Chunk数据是存储于同一mdat box中,按添加到Chunk链表时间先后顺序排列。等到录像结束时,录像应用会调用MPEG4Writer的stop方法,此时就会将音视频的trak表分别写入moov。

    图 2 MPEG4Writer封装数据流图

    二、MPEG4Writer.cpp跟读

    1、构造函数

    构造函数,MPEG4Writer::MPEG4Writer,实现一些参数的初始化,fd是传进来的录制文件的文件描述符。

    MPEG4Writer::MPEG4Writer(int fd) {    
        initInternal(fd, true /*isFirstSession*/);
    }

    2、start()

    应用层 MediaRecorder.start();时,往framework层调用时,将会调用到MPEG4Writer.cpp 中的 start部分,在start部分,我们看到在这一部分,writeFtypBox(param) 将实现录制文件文件头部信息的相关信息的写入操作;startWriterThread() 开启封装视频文件的写线程;startTracks(param) 开启视频数据的读线程,也就是前面文件部分所说的轨迹线程。

    status_t MPEG4Writer::start(MetaData *param) {
        …
        //暂停后,再次start时,mStarted = true;mPaused = false;
        if (mStarted) {
            if (mPaused) {
                mPaused = false;
                return startTracks(param);
            }
            return OK;
        }
        …
        writeFtypBox(param); //写入封装文件头部信息
        mFreeBoxOffset = mOffset;
        …
        mOffset = mMdatOffset; 
        lseek64(mFd, mMdatOffset, SEEK_SET); //将文件指针移动到mMdatOffset的位置
        status_t err = startWriterThread();  //开启封装视频文件的写线程
        err = startTracks(param);            //开启视频数据的读线程
        mStarted = true;
    }

    3、startWriterThread()

    看下 startWriterThread()部分,在startWriterThread()函数中,将真正建立新的子线程,并在子线程中执行ThreadWrapper()函数中的操作。

    status_t MPEG4Writer::startWriterThread() {
        mDone = false;
        mIsFirstChunk = true;
        mDriftTimeUs = 0;
        for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) {
            ChunkInfo info;
            info.mTrack = *it;
            info.mPrevChunkTimestampUs = 0;
            info.mMaxInterChunkDurUs = 0;
            mChunkInfos.push_back(info);  //
        }
    
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
        pthread_create(&mThread, &attr,ThreadWrapper, this);
        pthread_attr_destroy(&attr);
        mWriterThreadStarted = true;
        return OK;
    }
    

    3.1 ThreadWrapper()

    接着继续看 ThreadWrapper()函数,在这里new 了一个MPEGWriter对象,真正的操作在threadFunc()中体现。

    void *MPEG4Writer::ThreadWrapper(void *me) {
        MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
        writer->threadFunc();
        return NULL;
    }

    3.2 threadFun()

    下面看下threadFun()。在这个函数中,将根据变量mDone 进行while循环,一直检测是否有数据块Chunk可写。轨迹线程是一直将读数据的数据往buffer中写入,buffer到了一定量后,就是chunk,这时就会通过信号量 mChunkReadyCondition来通知封装文件的写线程去检测链表,然后将检索到的Chunk数据写入文件的数据区,当然写之前,肯定会去判断下是否真的有数据可写。

    void MPEG4Writer::threadFunc() {
        prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
        Mutex::Autolock autoLock(mLock);
        while (!mDone) {  
            Chunk chunk;
            bool chunkFound = false;
            while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
                mChunkReadyCondition.wait(mLock);
            }
    
            // In real time recording mode, write without holding the lock in order
            // to reduce the blocking time for media track threads.
            // Otherwise, hold the lock until the existing chunks get written to the
            // file.
            if (chunkFound) {
                if (mIsRealTimeRecording) {
                    mLock.unlock();
                }
                writeChunkToFile(&chunk);
                if (mIsRealTimeRecording) {
                    mLock.lock();
                }
            }
        }
    
        writeAllChunks();//应用层MediaRecorder.stop()时,mDone的值将为true。
    }

    3.3 writerChunkToFile(&chunk)

    下面看下writerChunkToFile(&chunk);轨迹线程读数据时是以数据帧Sample为单位,所以这里将Chunk写入封装文件,也是以Sample为单位,遍历整个链表,将数据写入封装文件,真正的写入操作是addSample_l(*it);

    void MPEG4Writer::writeChunkToFile(Chunk* chunk) {
        int32_t isFirstSample = true;
        while (!chunk->mSamples.empty()) {
            List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
            off64_t offset = chunk->mTrack->isAvc()
                                    ? addLengthPrefixedSample_l(*it)
                                    :addSample_l(*it);
    
            if (isFirstSample) {
                chunk->mTrack->addChunkOffset(offset);
                isFirstSample = false;
            }
    
            (*it)->release();
            (*it) = NULL;
            chunk->mSamples.erase(it);
        }
        chunk->mSamples.clear();
    }

    3.4 addSamole_l(*it)

    下面看下 addSamole_l(*it) 函数,wirte写入操作,mFd 是上层设置录制的文件路径传下来的文件描述符。

    off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer) {
        off64_t old_offset = mOffset;
        ::write(mFd,
              (const uint8_t *)buffer->data() + buffer->range_offset(),
              buffer->range_length());
    
        mOffset += buffer->range_length();
    
        return old_offset;
    }
    

    到此,封装文件的写入线程的操作大体走完,下面看轨迹线程的操作。

    4、startTracks(param)

    startTracks(param) 轨迹线程的开启。文件的录制过程中是有2条轨迹线程,一个是视频的轨迹线程,另一条则是音频的轨迹线程,在starTrack(param)中是在for 循环中start了两条轨迹线程。

    status_t MPEG4Writer::startTracks(MetaData *params) {
        for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) {
            status_t err = (*it)->start(params);
        }
    }

    (*it)->start(params) 将会执行status_t MPEG4Writer::Track::start(MetaData *params) {} 。在这边也是同样新建子线程,在子线程中执行轨迹线程的相应操作。

    status_t MPEG4Writer::Track::start(MetaData *params) {
        pthread_create(&mThread, &attr, ThreadWrapper, this);
    }

    真正的操作又是放到了threadEntry()中去执行。

    void *MPEG4Writer::Track::ThreadWrapper(void *me) {
        Track *track = static_cast<Track *>(me);
        status_t err = track->threadEntry();
        return (void *) err;
    }
    

    录制文件结束时,上层应用分别是调用 MediaRecorder的stop()、reset()和release()方法,下面看下MPEG4Writer.cpp中相对应的操作。

    status_t MPEG4Writer::Track::stop() {
        status_t status = mSource->stop();
    }

    reset()函数中将完成轨迹线程、写入线程的停止、封装文件尾部信息的写入等操作。

    status_t MPEG4Writer::reset() {
        for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) {
            status_t status = (*it)->stop();//停止轨迹线程
        }
        stopWriterThread(); //停止封装文件的写线程
        writeMoovBox(maxDurationUs);//封装文件尾部相应信息的写入
        write("free", 4);
        release();
    }

    release()中关闭文件描述符。

    void MPEG4Writer::release() {
        close(mFd);
        mFd = -1;
        mInitCheck = NO_INIT;
        mStarted = false;
    }

    至此,整个录制文件的封装过程,大体流程走完,就是有轨迹线程不停的读取数据,然后读取的数据达到一定的大小时,也就是成chunk(块)时,将会通知写入线程去检测是否有chunk可写,有的话,将进行数据的写入操作。整个封装文件必须有相应的头部信息和尾部信息。

     

    参考:

    https://blog.csdn.net/liwendovo/article/details/8478259

    http://www.voidcn.com/article/p-sdudnogn-bce.html

    展开全文
  • 硕士毕业论文《MPEG4-ASP视频编码器算法设计及DSP实现》:研究和实现了MPEG4-ASP视频编码标准的关键算法——运动估计和全局运动估计,掌握了视频压缩编码的核心技术以及在通用DSP平台上实现视频编码器的技术要点并...
  • MPEG4Extractor分析

    2019-04-29 17:41:09
    Android Stagefright MPEG4Extractor分析 视频播放的基本流程 播放器从DataSource获取媒体数据,通过Demuxer分离音视频轨道,分别送到相应的音视频解码器,最后将解码后的数据输出到音视频设备。 在Stage...

    Android Stagefright MPEG4Extractor分析

    视频播放的基本流程

     

     

    播放器从DataSource获取媒体数据,通过Demuxer分离音视频轨道,分别送到相应的音视频解码器,最后将解码后的数据输出到音视频设备。

    Stagefright里,MediaExtractor即是用于分离音视频轨道的Demuxer。它是一个抽象类,声明了方法

    sp<MediaSource> getTrack(size_t index)

    用于获取分离后的音视频流(track)。具体的逻辑则由不同的子类依据媒体文件容器的格式实现。

    class MediaExtractor : public RefBase {

    public:

        static sp<MediaExtractor> Create(

                const sp<DataSource> &source, const char *mime = NULL);

     

        virtual size_t countTracks() = 0;

        virtual sp<MediaSource> getTrack(size_t index) = 0;

        virtual sp<MetaData> getTrackMetaData(

            size_t index, uint32_t flags = 0) = 0;

     

        virtual sp<MetaData> getMetaData();

    ...

    };

    MPEG4ExtractorMediaExtractor的子类,用于解析MP4格式的媒体文件。

    MP4文件是由一系列的Box构成的,BoxHeader包含两个属性size,type,指明Box的大小和类型。BoxBody可以仅是包含其它的Box(容器Box,也可以仅包含数据(叶子Box)。这种结构与XML类似,不同的是XMLTag是文本数据,MP4Box是二进制数据。

     

    主要包含如下Box类型,层次关系由缩进表示。

    moov

      mvhd

      trak

        tkhd

        edts

          elst

        mdia

          mdhd

          minf

            stbl

              stsd

              stco

              co64

              stts

              stss

              stsc

              stsz

      trak

      trak

      ..

    mdat

      [data]

      [data]

      [...]

    The moov atom contains instructions for playing the data in the file. The mdat atom contains the data that will be played.

    •  
      MPEG4Extractor的主要功能即是把Movie Box(moov)的信息解析出来,以便在播放的时候能够根据这些信息找到正确的媒体数据。比较重要的数据结构是Track(对应于trak Box)和它包含的SampleTable(对应于stbl Box和其子Box:stsd,stco,co64,stts,stss,stsc,stsz)
      class MPEG4Extractor : public MediaExtractor {
      public:
          // Extractor assumes ownership of "source".
          MPEG4Extractor(const sp<DataSource> &source);
       
          virtual size_t countTracks();    // 轨道数量
          virtual sp<MediaSource> getTrack(size_t index);    // 获取轨道
          virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);    // 获取轨道的元数据,如:需要的解码器
       
          virtual sp<MetaData> getMetaData();    // 获取媒体文件容器的元数据,如MimeType
       
      protected:
          virtual ~MPEG4Extractor();
       
      private:
          // 轨道元数据
          struct Track {
              Track *next;
              sp<MetaData> meta;
              uint32_t timescale;
              sp<SampleTable> sampleTable;
              bool includes_expensive_metadata;
              bool skipTrack;
          };
       
          sp<DataSource> mDataSource;
          bool mHaveMetadata;
          bool mHasVideo;
         
          // 轨道链表的头节点和尾节点
          Track *mFirstTrack, *mLastTrack;
       
          sp<MetaData> mFileMetaData;
       
          Vector<uint32_t> mPath;
       
          status_t readMetaData();
          // 解析MP4文件,生成track链表
          status_t parseChunk(off_t *offset, int depth);
          // 解析MP4文件中的扩展信息,如:艺术家,专辑,流派等
          status_t parseMetaData(off_t offset, size_t size);
       
          status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
                  const void *esds_data, size_t esds_size);
       
          static status_t verifyTrack(Track *track);
          // 解析 box 'tkhd'
          status_t parseTrackHeader(off_t data_offset, off_t data_size);
       
          MPEG4Extractor(const MPEG4Extractor &);
          MPEG4Extractor &operator=(const MPEG4Extractor &);
      };
      getTrack方法返回的是MediaSource的子类MPEG4Source的实例,其read方法从分离后的轨道中读取未解码的媒体数据。
      sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
          status_t err;
          if ((err = readMetaData()) != OK) {
              return NULL;
          }
       
          Track *track = mFirstTrack;
          while (index > 0) {
              if (track == NULL) {
                  return NULL;
              }
       
              track = track->next;
              --index;
          }
       
          if (track == NULL) {
              return NULL;
          }
       
          return new MPEG4Source(
                  track->meta, mDataSource, track->timescale, track->sampleTable);
      }
      readMetaData方法中调用parseChunk方法对MP4文件进行解析,解析后生成的数据结构(主要是Track)用于创建MPEG4Source.
      status_t MPEG4Extractor::readMetaData() {
          if (mHaveMetadata) {
              return OK;
          }
       
          off_t offset = 0;
          status_t err;
          while ((err = parseChunk(&offset, 0)) == OK) {
          }
       
          if (mHaveMetadata) {
              if (mHasVideo) {
                  mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
              } else {
                  mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
              }
       
              return OK;
          }
       
          return err;
      }
      parseChunk方法根据读取到的Box type创建相应的数据结构,抽取Box包含的信息。
      创建Track,并加入到Track链表的尾部。
      if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
          isTrack = true;
       
          Track *track = new Track;
          track->next = NULL;
          if (mLastTrack) {
              mLastTrack->next = track;
          } else {
              mFirstTrack = track;
          }
          mLastTrack = track;
          ...
      }
      创建Track包含的SampleTableSampleTable里包含SampleIterator用于seek和获取sample在文件中的位置和大小。
      if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
          LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size);
       
          if (mDataSource->flags()
                  & (DataSource::kWantsPrefetching
                      | DataSource::kIsCachingDataSource)) {
              sp<MPEG4DataSource> cachedSource =
                  new MPEG4DataSource(mDataSource);
       
              if (cachedSource->setCachedRange(*offset, chunk_size) == OK) {
                  mDataSource = cachedSource;
              }
          }
       
          mLastTrack->sampleTable = new SampleTable(mDataSource);
      }
      解析Chunk offset Box
      case FOURCC('s', 't', 'c', 'o'):
      case FOURCC('c', 'o', '6', '4'):
      {
          status_t err =
              mLastTrack->sampleTable->setChunkOffsetParams(
                      chunk_type, data_offset, chunk_data_size);
       
          if (err != OK) {
              return err;
          }
       
          *offset += chunk_size;
          break;
      }
      解析Sample to chunk Box
      case FOURCC('s', 't', 's', 'c'):
      {
          status_t err =
              mLastTrack->sampleTable->setSampleToChunkParams(
                      data_offset, chunk_data_size);
       
          if (err != OK) {
              return err;
          }
       
          *offset += chunk_size;
          break;
      }
      解析Sample size Box
      case FOURCC('s', 't', 's', 'z'):
      case FOURCC('s', 't', 'z', '2'):
      {
          status_t err =
              mLastTrack->sampleTable->setSampleSizeParams(
                      chunk_type, data_offset, chunk_data_size);
       
          if (err != OK) {
              return err;
          }
       
          size_t max_size;
          err = mLastTrack->sampleTable->getMaxSampleSize(&max_size);
       
          if (err != OK) {
              return err;
          }
       
          // Assume that a given buffer only contains at most 10 fragments,
          // each fragment originally prefixed with a 2 byte length will
          // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
          // and thus will grow by 2 bytes per fragment.
          mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2);
       
          *offset += chunk_size;
          break;
      }
      解析Time to sample Box
      case FOURCC('s', 't', 't', 's'):
      {
          status_t err =
              mLastTrack->sampleTable->setTimeToSampleParams(
                      data_offset, chunk_data_size);
       
          if (err != OK) {
              return err;
          }
       
          *offset += chunk_size;
          break;
      }
      拥有以上信息之后MPEG4Sourceread方法便可以对任意时间点的媒体数据进行读取。
      Apple QuickTime File Format Specification给出了使用Sample table box的方法(链接
      Finding a Sample
      When QuickTime displays a movie or track, it directs the appropriate media handler to access the media data for a particular time. The media handler must correctly interpret the data stream to retrieve the requested data. In the case of video media, the media handler traverses several atoms to find the location and size of a sample for a given media time.
      The media handler performs the following steps:
    1. Determines the time in the media time coordinate system.
    2. Examines the time-to-sample atom to determine the sample number that contains the data for the specified time.
    3. Scans the sample-to-chunk atom to discover which chunk contains the sample in question.
    4. Extracts the offset to the chunk from the chunk offset atom.
    5. Finds the offset within the chunk and the sample’s size by using the sample size atom.

     

    Finding a Key Frame

    Finding a key frame for a specified time in a movie is slightly more complicated than finding a sample for a specified time. The media handler must use the sync sample atom and the time-to-sample atom together in order to find a key frame.

    The media handler performs the following steps:

    1. Examines the time-to-sample atom to determine the sample number that contains the data for the specified time.
    2. Scans the sync sample atom to find the key frame that precedes the sample number chosen in step 1.
    3. Scans the sample-to-chunk atom to discover which chunk contains the key frame.
    4. Extracts the offset to the chunk from the chunk offset atom.
    5. Finds the offset within the chunk and the sample’s size by using the sample size atom.

    read方法里首先检查是否有seek option,如果有,则根据seek到的时间点找到对应的sample index,并找到该sample index之前的关键帧,设置为当前帧mCurrentSampleIndex

    if (options && options->getSeekTo(&seekTimeUs, &mode)) {

        uint32_t findFlags = 0;

        switch (mode) {

            case ReadOptions::SEEK_PREVIOUS_SYNC:

                findFlags = SampleTable::kFlagBefore;

                break;

            case ReadOptions::SEEK_NEXT_SYNC:

                findFlags = SampleTable::kFlagAfter;

                break;

            case ReadOptions::SEEK_CLOSEST_SYNC:

            case ReadOptions::SEEK_CLOSEST:

                findFlags = SampleTable::kFlagClosest;

                break;

            default:

                CHECK(!"Should not be here.");

                break;

        }

     

        uint32_t sampleIndex;

        // 通过 time to sample box 找到与时间点对应的 sampleIndex

        status_t err = mSampleTable->findSampleAtTime(

                seekTimeUs * mTimescale / 1000000,

                &sampleIndex, findFlags);

     

        if (mode == ReadOptions::SEEK_CLOSEST) {

            // We found the closest sample already, now we want the sync

            // sample preceding it (or the sample itself of course), even

            // if the subsequent sync sample is closer.

            findFlags = SampleTable::kFlagBefore;

        }

     

        uint32_t syncSampleIndex;

        if (err == OK) {

            // 找到sampleIndex之前的一个关键帧syncSampleIndex

            err = mSampleTable->findSyncSampleNear(

                    sampleIndex, &syncSampleIndex, findFlags);

        }

     

        if (err != OK) {

            if (err == ERROR_OUT_OF_RANGE) {

                // An attempt to seek past the end of the stream would

                // normally cause this ERROR_OUT_OF_RANGE error. Propagating

                // this all the way to the MediaPlayer would cause abnormal

                // termination. Legacy behaviour appears to be to behave as if

                // we had seeked to the end of stream, ending normally.

                err = ERROR_END_OF_STREAM;

            }

            return err;

        }

     

        uint32_t sampleTime;

        CHECK_EQ((status_t)OK, mSampleTable->getMetaDataForSample(

                    sampleIndex, NULL, NULL, &sampleTime));

     

        if (mode == ReadOptions::SEEK_CLOSEST) {

            targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;

        }

     

        uint32_t syncSampleTime;

        CHECK_EQ(OK, mSampleTable->getMetaDataForSample(

                    syncSampleIndex, NULL, NULL, &syncSampleTime));

     

        LOGI("seek to time %lld us => sample at time %lld us, "

             "sync sample at time %lld us",

             seekTimeUs,

             sampleTime * 1000000ll / mTimescale,

             syncSampleTime * 1000000ll / mTimescale);

        // 设置当前mCurrentSampleIndex的值为关键帧syncSampleIndex(因为seek之后送给解码器的第一帧需要是关键帧)

        mCurrentSampleIndex = syncSampleIndex;

        if (mBuffer != NULL) {

            mBuffer->release();

            mBuffer = NULL;

        }

     

        // fall through

    }

    调用SampleTablegetMetaDataForSample方法获取sample的文件偏移量和大小,以及解码时间戳,是否是关键帧。

    off_t offset;

    size_t size;

    uint32_t dts;

    bool isSyncSample;

    bool newBuffer = false;

    if (mBuffer == NULL) {

        newBuffer = true;

        // 获取当前 sample 的文件偏移量和大小,解码时间戳,是否关键帧

        status_t err =

            mSampleTable->getMetaDataForSample(

                    mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample);

     

        if (err != OK) {

            return err;

        }

     

        err = mGroup->acquire_buffer(&mBuffer);

     

        if (err != OK) {

            CHECK(mBuffer == NULL);

            return err;

        }

    }

    有了offsetsize之后就可以将未解码的数据读入buffer,推给解码器。Sample读取完成之后mCurrentSampleIndex加一,准备下一次read调用。

    下面重点分析SampleTable#getMetaDataForSample的实现。该方法一开始调用SampleIterator#seekTo方法定位到给定的sample index,设置 mCurrentSampleOffset, mCurrentSampleSize.

    //

    // set the following instance variable

    // * mCurrentSampleOffset

    // * mCurrentSampleSize

    status_t SampleIterator::seekTo(uint32_t sampleIndex) {

        LOGV("seekTo(%d)", sampleIndex);

     

        if (sampleIndex >= mTable->mNumSampleSizes) {

            return ERROR_END_OF_STREAM;

        }

     

        if (mTable->mSampleToChunkOffset < 0

                || mTable->mChunkOffsetOffset < 0

                || mTable->mSampleSizeOffset < 0

                || mTable->mTimeToSampleCount == 0) {

     

            return ERROR_MALFORMED;

        }

       

        if (mInitialized && mCurrentSampleIndex == sampleIndex) {

            return OK;

        }

        // 如果 sampleIndex 不在当前的 sample-to-trunk block 里,则重置

        if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) {

            reset();

        }

     

        // 3. Scans the sample-to-chunk atom to discover which chunk contains the sample in question.

        if (sampleIndex >= mStopChunkSampleIndex) {

            status_t err;

            if ((err = findChunkRange(sampleIndex)) != OK) {

                LOGE("findChunkRange failed");

                return err;

            }

        }

     

        CHECK(sampleIndex < mStopChunkSampleIndex);

       

        // shaobin: get the current chunk index which contain the given sample

        uint32_t chunk =

            (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk

            + mFirstChunk;

     

        if (!mInitialized || chunk != mCurrentChunkIndex) {

            mCurrentChunkIndex = chunk;

     

            // 4. Extracts the offset of current chunk from the chunk offset atom.

            status_t err;

            if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {

                LOGE("getChunkOffset return error");

                return err;

            }

     

            mCurrentChunkSampleSizes.clear();

     

            // the first sample index in current chunk

            uint32_t firstChunkSampleIndex =

                mFirstChunkSampleIndex

                    + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);

     

            for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {

                size_t sampleSize;

                if ((err = getSampleSizeDirect(

                                firstChunkSampleIndex + i, &sampleSize)) != OK) {

                    LOGE("getSampleSizeDirect return error");

                    return err;

                }

     

                mCurrentChunkSampleSizes.push(sampleSize);

            }

        }

     

        // the sample index offset within current chunk

        uint32_t chunkRelativeSampleIndex =

            (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk;

     

        // 5. Finds the offset within the chunk and the sample's size by using the sample size atom.

        mCurrentSampleOffset = mCurrentChunkOffset;

        for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) {

            mCurrentSampleOffset += mCurrentChunkSampleSizes[i];

        }

     

        mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex];

        if (sampleIndex < mTTSSampleIndex) {

            mTimeToSampleIndex = 0;

            mTTSSampleIndex = 0;

            mTTSSampleTime = 0;

            mTTSCount = 0;

            mTTSDuration = 0;

        }

     

        status_t err;

        // set mCurrentSampleTime with the given sampleIndex

        if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {

            LOGE("findSampleTime return error");

            return err;

        }

     

        mCurrentSampleIndex = sampleIndex;

     

        mInitialized = true;

     

        return OK;

    }

    SampleIterator#findChunkRange用于在Sample-to-Chunk Box中查找包含给定sample indexChunk集合,设置如下属性

    • mFirstChunk
    • mFirstChunkSampleIndex
    • mStopChunk
    • mStopChunkSampleIndex
    • mSamplesPerChunk
      //
      // shaobin: this method is used to set
      // * mFirstChunkSampleIndex
      // * mFirstChunk
      // * mStopChunk
      // * mStopChunkSampleIndex
      // * mSamplesPerChunk
      status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
          CHECK(sampleIndex >= mFirstChunkSampleIndex);
       
          while (sampleIndex >= mStopChunkSampleIndex) {
              if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) {
                  return ERROR_OUT_OF_RANGE;
              }
       
              mFirstChunkSampleIndex = mStopChunkSampleIndex;
       
              const SampleTable::SampleToChunkEntry *entry =
                  &mTable->mSampleToChunkEntries[mSampleToChunkIndex];
       
              mFirstChunk = entry->startChunk;
              mSamplesPerChunk = entry->samplesPerChunk;
              mChunkDesc = entry->chunkDesc;
       
              if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
                  mStopChunk = entry[1].startChunk;    // the next Sample-to-Chunk entry
       
                  mStopChunkSampleIndex =
                      mFirstChunkSampleIndex
                          + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
              } else {
                  mStopChunk = 0xffffffff;
                  mStopChunkSampleIndex = 0xffffffff;
              }
       
              ++mSampleToChunkIndex;
          }
       
          return OK;
      }
      举个例子
      ?

                       |  ....  |

           mFirstChuck +========+ mFirstChunkSampleIndex

                       |--------|

                       |--------|

    mCurrentChunkIndex +========+ firstChunkSampleIndex

                       |--------|

                       |--------| sampleIndex(chunkRelativeSampleIndex)

                       +========+

                       |--------|

                       |--------|

            mStopChunk +========+ mStopChunkSampleIndex

                       |  ....  |

    mSamplesPerChunk = 3;

    mCurrentChunkIndex = mFirstChunk + (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk;

    firstChunkSampleIndex = mFirstChunkSampleIndex + (mCurrentChunkIndex - mFirstChunk) * mSamplesPerChunk;

    chunkRelativeSampleIndex = (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerchunk;

     

    展开全文
  • 摘 要 详细阐述了针对ARM平台的MPEG4视频解码算法的优化方法。实验数据表明,优化后的解码器性能得到了全面提升。还结合ARM7TDMI的Easy ARM2200开发平台,给出了嵌入式MPEG-4视频解码的实时实现。 关键词 ARM,...
  • ffmpeg编码成mpeg4或h264文件

    热门讨论 2012-05-28 15:12:05
    ffmpeg编码成mpeg4或h264文件
  • 封装h264,/mpeg4 为MP4文件

    热门讨论 2012-12-21 15:20:59
    封装了开源工程, mp4v2, mpeg4ip代码, /******************************************/ /* Name:Mp4Interface.h /* Mark:mp4封装解析接口 /* author: machh /* date:2012.5.12. /****************************...
  • 包含MPEG4的所有标准文档: MPEG4_Systems_CD_w1901.doc; MPEG4_v1_Overview_W1909.doc; MPEG4_v2_Visual_WD2_W1993.doc; MPEG4_Video_VM10_W1992.doc; MPEG4_Visual_CD_w1902.doc
  • H264和MPEG4区别

    千次阅读 2020-12-15 10:00:34
    H.264的特征优势 1、低码流:和MPEG2和MPEG4 ASP等压缩技术相比,在同等图像质量下,采用H.264技术压缩后的数据量只有MPEG2的1/8,MPEG4的1/3。 显然,H.264压缩技术的采用将大大节省用户的下载时间和数据流量收费...

    MPEG-4编码技术
    MPEG-4:MPEG-4是一个适用于低传输速率应用的方案,MPEG-4是在MPEG-1、MPEG-2基础上发展而来,是为了播放流式媒体的高质量视频而专门设计的,它可利用很窄的带度,通过帧重建技术,压缩和传输数据,以求使用最少的数据获得最佳的图像质量。
    MPEG-4标准则是基于对象和内容的编码方式,和传统的图像帧编码方式不同,它只处理图像帧与帧之间的差异元素,抛弃相同图像元素,因此大大减少了合成多媒体文件的体积,从而以较小的文件体积同样可得到高清晰的还原图像。换句话说,相同的原始图像,MPEG-4编码标准具有更高的压缩比。

    H.264编码技术
    H.264是ITU-T国际电联与ISO国际标准化组织联合制定的视频编解码技术标准,h.264是一种高性能的视频编解码技术。
    H.264最大的优势是具有很高的数据压缩比率,在同等图像质量的条件下,H.264的压缩比是MPEG-2的2倍以上,是MPEG-4的1.5~2倍。
    一个原始文件是102G大小的视频,经过H.264编码后变成了1个G,压缩比竟达到了102:1。因此H.264的低码率技术起到了至关重要的作用,
    在用户获得高质量流畅图像的同时,大大节省了下载时间和数据流量,也大大减少了图像存储空间。
    H.264是在MPEG-4技术的基础之上建立起来的,其编解码流程主要包括5个部分:帧间和帧内预测(Estimation)、变换(Transform)和反变换、量化(Quantization)和反量化、环路滤波(Loop Filter)、熵编码(Entropy Coding)。

    视频监控技术经过多年的发展,监控画面正经历着从最初的D1标清图像,向4K高清、8K超清时代前进。前端像素的提高给视频传输和后端录像存储带来了巨大的压力,在相同的编码压缩比例下,用户需要投入更多的设备和资金,因此编解码技术发展近些年也是突飞猛进。由于H.264的高压缩性能,目前市场上主流的视频基本采用的是H.264标准。
    H.264的特征优势
    1、低码流:和MPEG2和MPEG4 ASP等压缩技术相比,在同等图像质量下,采用H.264技术压缩后的数据量只有MPEG2的1/8,MPEG4的1/3。
    显然,H.264压缩技术的采用将大大节省用户的下载时间和数据流量收费。
    2、高质量的图象:H.264能提供连续、流畅的高质量图象(DVD质量)。
    3、容错能力强:H.264提供了解决在不稳定网络环境下容易发生的丢包等错误的必要工具。
    4、网络适应性强:H.264提供了网络适应层, 使得H.264的文件能容易地在不同网络上传输(例如互联网,CDMA,GPRS,WCDMA,CDMA2000等)。
    扩展阅读:
    H.265编码技术

    H.265技术是ITU-T VCEG 继H.264之后所制定的新的视频编码标准。H.265标准围绕现有的视频编码标准H.264,保留原来的某些技术,同时对一些相关的技术加以改进。新技术用以改善码流、编码质量、延时和算法复杂度之间的关系,达到最优化设置。H.264由于算法优化,可以低于1Mbps的速度实现标清数字图像传送,H.265则可以实现利用1~2Mbps的传输速度实现720P(分辨率1280*720)普通高清音视频传送。
    目前,H.265成为视频主流编解码技术。H.265对比传统H.264,码率降低50%,压缩比高。
    H.265旨在在有限带宽下传输更高质量的网络视频,仅需原先的一半带宽即可播放相同质量的视频。H.265标准也同时支持4K(4096×2160)和8K(8192×4320)超高清视频。可以说,H.265标准让网络视频跟上了显示屏“高分辨率化”的脚步。千视作为音视频IP化传输解决方案专家,H.265或H.264音视频传输解决方案都有。

    H.266编码技术
    随着全球互联网视频需求的增长,MPEG 正在推动 H.266 / VCC 以及其它两个标准的发展。其中 MPEG-5 第一部分又被称作基础视频编码(EVC),第二部分又被称作低复杂度增强视频编码(LCEVC)。
    H.266多功能视频编码编解码器标准,新的H.266 /VCC编解码器在保持清晰度不变的情况下,数据压缩效率获得极大提高,数据量减少了50%。
    H.266将减少约50%的数据需求。使用之前的HEVC编解码器,传输一段90分钟的超高清(UHD)视频需要大约10GB的数据,而H.266只需5GB就可以做到这一点。H.266主要用于4K和8K流媒体视频,它将允许用户存储更多的高清视频,并减少移动网络的数据量。

    展开全文
  • MPEG4编码库源代码,适合MPEG4编程使用。
  • 一种MPEG4视频压缩和传输系统 王 博,马宏绪,李 迅 (国防科学技术大学机电工程与自动化学院机器人教研室 湖南长沙 410073) 在重要场所安全监控或工业现场控制系统中,直观、方便且内容丰富的视频实时采集是信息采集...
  • mpeg4视频解码源码

    2015-06-08 21:44:53
    mpeg4的c语言源代码及数字压缩和编码标准ISO/IEC 10918-1
  • ---众所周知,DSP在移动电话等便携机器领域里已成为实时处理语音编码/译码、语音...随着便携式AV 机器的推广应用,愈来愈要求能很容易地向存储器里记录那些按照MPEG4压缩的信息。随着通信带宽的拓展,这些AV 机器的功能
  • 闪电MPEG4格式转换器是一个功能强大的MPEG4格式转换工具,可以帮助您将各种流行的视频格式转换为MPEG4视频格式。如将RM、RMVB、AVI、VOB、FLV、MKV、DAT、VCD、DVD、SVCD、ASF、MOV、QT、MPEG、WMV、MP4、3GP、DivX...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,413
精华内容 8,165
关键字:

mpeg4