MediaExtractor MediaMuxer 实现视频的解封装与合成

这篇具有很好参考价值的文章主要介绍了MediaExtractor MediaMuxer 实现视频的解封装与合成。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、MediaExtractor MediaMuxer 能做什么,有什么实际应用

1.1 视频解封装和合成的API以及流程介绍

1.1.1 MediaExtractor:视频轨道提取器(解封装)

1.1.2  MediaMuxer:合成(封装)

1.2 实践(以及ffmpeg的实现)

1.2.1. 提取视频分离出纯音频和纯视频文件*

1.2.2. 把纯音频文件和纯视频文件(封装)合成为视频文件

1.2.3. 替换背景音乐,合成新的视频文件

1.2.4 遇到的问题


1、MediaExtractor MediaMuxer 能做什么,有什么实际应用

在我们日常使用短视频软件的时候,对视频的裁剪,拼凑,加入背景是很常用的操作,这些功能是如何实现的呐?其实是将视频多信道的分离出来,比如音轨和视频轨道分隔出来,可以做到二次合成。

今天我们通过对来MediaExtractor和MediaMuxer的学习分析和实践来实现 “把视频分离(提取&解封装)出纯音频和纯视频文件”、“替换背景音乐,合成新的视频文件”。

1.1 视频解封装和合成的API以及流程介绍

1.1.1 MediaExtractor:视频轨道提取器(解封装)

**主要API介绍**

-  setDataSource(path):path本地或者网络文件

- getTrackCount:获取轨道数

-  getTrackFormat(i):对应轨道的格式 MediaFormat

-  selectTrack(I):切换到(选定)某个轨道

- readSampleData(ByteBuffer byteBuff, int offset): 把指定轨道中的样本数据按偏移量读取到ByteBuffer字节缓冲区

- advance():  提取到下一帧数据  作用有点类似于cursor

- unselectTrack(i)

-  release()

- getSampleFlags: 获取数据的flag,数据为什么要用Sample来表示,因为音视频的数据是采样数据。

- getSampleTime:返回当前的时间戳

**数据提取(解封装)流程如下:**

//1. 构造MediaExtractor
MediaExtractor mediaExtractor = new MediaExtractor();
try {
    //2.设置数据源
    mediaExtractor.setDataSource(inputFile.getAbsolutePath());
    //3. 获取轨道数
    int trackCount = mediaExtractor.getTrackCount();
    Log.i(TAG, "demuxerMP4: trackCount=" + trackCount);
    //遍历轨道,查看音频轨或者视频轨道信息
    for (int i = 0; i < trackCount; i++) {
        //4. 获取某一轨道的媒体格式
        MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
        String keyMime = trackFormat.getString(MediaFormat.KEY_MIME);
        Log.i(TAG, "demuxerMp4: keyMime=" + keyMime);
        if (TextUtils.isEmpty(keyMime)) {
            continue;
        }
        //5.通过mime信息识别音轨或视频轨道,打印相关信息
        if (keyMime.startsWith("video/")) {
            //打印视频的宽高
            Log.i(TAG, "extractorAndMuxerMP4:                     videoWidth="+trackFormat.getInteger(MediaFormat.KEY_WIDTH)+" videoHeight="+trackFormat.getInteger(MediaFormat.KEY_HEIGHT));
        } else if (keyMime.startsWith("audio/")) {
            //打印音轨的通道数以及比特率
            Log.i(TAG, "extractorAndMuxerMP4: channelCount="+trackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)+" bitRate="+trackFormat.getInteger(MediaFormat.KEY_BIT_RATE));
        }
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    mediaExtractor.release();
}

1.1.2  MediaMuxer:合成(封装)

把音轨和视频轨道合成封装为新的视频

 **主要API介绍**

- MediaMuxer(path,format):path 输出文件的名称;foramt输出文件的格式,当前只支持mp4

- addTrack(trackFormat):添加轨道,通常是使用MediaCodec.getOutputForma()或MediaExtractor.getTrackFormat(int index)来获取MediaFormat

- start():开始封装合成

- writeSampleData (int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo): 把数据写入到

- stop()

- release()

**封装(合成)流程如下:**

{
    MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
    MediaMuxer mediaMuxer;
    mediaExtractor.selectTrack(i);
    //1. 构造MediaMuxer
    mediaMuxer = new MediaMuxer(outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    //2. 添加轨道信息 参数为MediaFormat
    mediaMuxer.addTrack(trackFormat);
    //3. 开始合成
    mediaMuxer.start();
    //4. 设置buffer
    ByteBuffer buffer = ByteBuffer.allocate(500 * 1024);
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    //5.通过mediaExtractor.readSampleData读取数据流
    int sampleSize = 0;
    while ((sampleSize = mediaExtractor.readSampleData(buffer, 0)) > 0) {
        bufferInfo.flags = mediaExtractor.getSampleFlags();
        bufferInfo.offset = 0;
        bufferInfo.size = sampleSize;
        bufferInfo.presentationTimeUs = mediaExtractor.getSampleTime();
        int isEOS = bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM;
        Log.i(TAG, "demuxerMp4:  flags=" + bufferInfo.flags + " size=" + sampleSize + " time=" + bufferInfo.presentationTimeUs + " outputName" + outputName+" isEOS="+isEOS);
        //6. 把通过mediaExtractor解封装的数据通过writeSampleData写入到对应的轨道
        mediaMuxer.writeSampleData(0, buffer, bufferInfo);
        mediaExtractor.advance();
    }
    Log.i(TAG, "extractorAndMuxer: " + outputName + "提取封装完成");
    mediaExtractor.unselectTrack(i);
    //6.关闭
    mediaMuxer.stop();
    mediaMuxer.release();
}

1.2 实践(以及ffmpeg的实现)

1.2.1. 提取视频分离出纯音频和纯视频文件*

private void extractorAndMuxerMP4() {
    tvOut.setText("");
    File inputFile = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), "forme.mp4");
    if (!inputFile.exists()) {
        Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show();
        return;
    }
    //数据提取(解封装)
    //1. 构造MediaExtractor
    MediaExtractor mediaExtractor = new MediaExtractor();
    try {
        //2.设置数据源
        mediaExtractor.setDataSource(inputFile.getAbsolutePath());
        //3. 获取轨道数
        int trackCount = mediaExtractor.getTrackCount();
        Log.i(TAG, "demuxerMP4: trackCount=" + trackCount);
        //遍历轨道,查看音频轨或者视频轨道信息
        for (int i = 0; i < trackCount; i++) {
            //4. 获取某一轨道的媒体格式
            MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
            String keyMime = trackFormat.getString(MediaFormat.KEY_MIME);
            Log.i(TAG, "demuxerMp4: keyMime=" + keyMime);
            if (TextUtils.isEmpty(keyMime)) {
                continue;
            }
            //5.通过mime信息识别音轨或视频轨道,打印相关信息
            if (keyMime.startsWith("video/")) {
                File outputFile = extractorAndMuxer(mediaExtractor, i, "/video.mp4");
                tvOut.setText("纯视频文件路径:" + outputFile.getAbsolutePath());
                Log.i(TAG, "extractorAndMuxerMP4: videoWidth="+trackFormat.getInteger(MediaFormat.KEY_WIDTH)+" videoHeight="+trackFormat.getInteger(MediaFormat.KEY_HEIGHT));
            } else if (keyMime.startsWith("audio/")) {
                File outputFile = extractorAndMuxer(mediaExtractor, i, "/audio.aac");
                Log.i(TAG, "extractorAndMuxerMP4: channelCount="+trackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)+" bitRate="+trackFormat.getInteger(MediaFormat.KEY_BIT_RATE));
                tvOut.setText(tvOut.getText().toString() + "\n纯音频路径:" + outputFile.getAbsolutePath());
                tvOut.setVisibility(View.VISIBLE);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        mediaExtractor.release();
    }
}

private File extractorAndMuxer(MediaExtractor mediaExtractor, int i, String outputName) throws IOException {
    MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
    MediaMuxer mediaMuxer;
    mediaExtractor.selectTrack(i);
    File outputFile = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC).getAbsolutePath() + outputName);
    if (outputFile.exists()) {
        outputFile.delete();
    }
    //1. 构造MediaMuxer
    mediaMuxer = new MediaMuxer(outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    //2. 添加轨道信息 参数为MediaFormat
    mediaMuxer.addTrack(trackFormat);
    //3. 开始合成
    mediaMuxer.start();
    //4. 设置buffer
    ByteBuffer buffer = ByteBuffer.allocate(500 * 1024);
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    //5.通过mediaExtractor.readSampleData读取数据流
    int sampleSize = 0;
    while ((sampleSize = mediaExtractor.readSampleData(buffer, 0)) > 0) {
        bufferInfo.flags = mediaExtractor.getSampleFlags();
        bufferInfo.offset = 0;
        bufferInfo.size = sampleSize;
        bufferInfo.presentationTimeUs = mediaExtractor.getSampleTime();
        int isEOS = bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM;
        Log.i(TAG, "demuxerMp4:  flags=" + bufferInfo.flags + " size=" + sampleSize + " time=" + bufferInfo.presentationTimeUs + " outputName" + outputName+" isEOS="+isEOS);
        //6. 把通过mediaExtractor解封装的数据通过writeSampleData写入到对应的轨道
        mediaMuxer.writeSampleData(0, buffer, bufferInfo);
        mediaExtractor.advance();
    }
    Log.i(TAG, "extractorAndMuxer: " + outputName + "提取封装完成");
    mediaExtractor.unselectTrack(i);
    //6.关闭
    mediaMuxer.stop();
    mediaMuxer.release();
    return outputFile;
}

1.2.2. 把纯音频文件和纯视频文件(封装)合成为视频文件

/**
     * 把音轨和视频轨再合成新的视频
     */
private String muxerMp4(String inputAudio , String outPutVideo) {
    File videoFile = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), "video.mp4");
    File audioFile = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), inputAudio);
    File outputFile = new File(getExternalFilesDir(Environment.DIRECTORY_MUSIC), outPutVideo);
    if (outputFile.exists()) {
        outputFile.delete();
    }
    if (!videoFile.exists()) {
        Toast.makeText(this, "视频源文件不存在", Toast.LENGTH_SHORT).show();
        return "";
    }
    if (!audioFile.exists()) {
        Toast.makeText(this, "音频源文件不存在", Toast.LENGTH_SHORT).show();
        return "";
    }

    MediaExtractor videoExtractor = new MediaExtractor();
    MediaExtractor audioExtractor = new MediaExtractor();

    try {
        MediaMuxer mediaMuxer = new MediaMuxer(outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        int videoTrackIndex = 0;
        int audioTrackIndex = 0;
        //先添加视频轨道
        videoExtractor.setDataSource(videoFile.getAbsolutePath());
        int trackCount = videoExtractor.getTrackCount();
        Log.i(TAG, "muxerToMp4: trackVideoCount=" + trackCount);
        for (int i = 0; i < trackCount; i++) {
            MediaFormat trackFormat = videoExtractor.getTrackFormat(i);
            String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
            if (TextUtils.isEmpty(mimeType)) {
                continue;
            }
            if (mimeType.startsWith("video/")) {
                videoExtractor.selectTrack(i);
                videoTrackIndex = mediaMuxer.addTrack(trackFormat);
                Log.i(TAG, "muxerToMp4: videoTrackIndex=" + videoTrackIndex);
                break;
            }
        }

        //再添加音频轨道
        audioExtractor.setDataSource(audioFile.getAbsolutePath());
        int trackCountAduio = audioExtractor.getTrackCount();
        Log.i(TAG, "muxerToMp4: trackCountAduio=" + trackCountAduio);
        for (int i = 0; i < trackCountAduio; i++) {
            MediaFormat trackFormat = audioExtractor.getTrackFormat(i);
            String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
            if (TextUtils.isEmpty(mimeType)) {
                continue;
            }
            if (mimeType.startsWith("audio/")) {
                audioExtractor.selectTrack(i);
                audioTrackIndex = mediaMuxer.addTrack(trackFormat);
                Log.i(TAG, "muxerToMp4: audioTrackIndex=" + audioTrackIndex);
                break;
            }
        }

        //再进行合成
        mediaMuxer.start();
        ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int sampleSize = 0;
        while ((sampleSize = videoExtractor.readSampleData(byteBuffer, 0)) > 0) {
            bufferInfo.flags = videoExtractor.getSampleFlags();
            bufferInfo.offset = 0;
            bufferInfo.size = sampleSize;
            bufferInfo.presentationTimeUs = videoExtractor.getSampleTime();
            mediaMuxer.writeSampleData(videoTrackIndex, byteBuffer, bufferInfo);
            videoExtractor.advance();
        }
        int audioSampleSize = 0;
        MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();

        while ((audioSampleSize = audioExtractor.readSampleData(byteBuffer, 0)) > 0) {
            audioBufferInfo.flags = audioExtractor.getSampleFlags();
            audioBufferInfo.offset = 0;
            audioBufferInfo.size = audioSampleSize;
            audioBufferInfo.presentationTimeUs = audioExtractor.getSampleTime();
            mediaMuxer.writeSampleData(audioTrackIndex, byteBuffer, audioBufferInfo);
            audioExtractor.advance();
        }
        //最后释放资源
        videoExtractor.release();
        audioExtractor.release();
        mediaMuxer.stop();
        mediaMuxer.release();
    } catch (IOException e) {
        e.printStackTrace();
        return "";
    }
    return outputFile.getAbsolutePath();
}

1.2.3. 替换背景音乐,合成新的视频文件

其实和第二步一样了,通过传入不同的aac音频源即可,这里需要注意一点,mediamuxer 只支持 aac 格式的,不支持mp3,否则会报如下异常,所以需要先把mp3转为aac。可以采用ffmpeg如下命令截取和转换

java.lang.IllegalStateException: Failed to add the track to the muxer
        at android.media.MediaMuxer.nativeAddTrack(Native Method)
        at android.media.MediaMuxer.addTrack(MediaMuxer.java:638)

—> 添加音轨不是aac格式,而是mp3格式时,在medimuter.addTrack(audioFromat)时会报上述错误,解决方案:把mp3转成aac
ffmpeg -i 输入.mp3 -acodec aac 输出.aac -y

1.2.4 遇到的问题

在合成写入数据时报 IllegalArgumentException: trackIndex is invalid**

java.lang.IllegalArgumentException: trackIndex is invalid
        at android.media.MediaMuxer.writeSampleData(MediaMuxer.java:669)
        —>  mediaMuxer.writeSampleData(0,buffer,bufferInfo);
原因和方案:  解封装时候输出的trackIndex不对导致,因为不管是纯音轨还是纯视频轨道文件只有一个轨道。

 解码出来的存视频文件的长度比原视频少了,而音频的长度一致。**

和视频源有关系,有的原视频最后几秒只有音频播放画面不动,就是这种情况,刚开时不知道,还以为是什么bug,最后通过ffmpeg直接对原视频进行提取,得到的结果一样。

用ffmpeg命令提取纯视频 对比看下

ffmpeg -i 输入.mp4 -vcodec copy -an 输出.mp4 -y 查看生成的视频也是一样。

说明这个视频中视频流就是比音频流要短。

ffmpeg -i 输入.mp4 -acodec copy -vn 输出.aac -y 查看生成的音频流。和通过medieExtractor和mediamuxter提取的一致。

mediaExtractor.advance()时报IllegalArgumentException: bufferInfo must specify a valid buffer**

通过查看bufferinfo的信息此时flags和presntationTimeUs都为-1,是advance调用时间不对引起

java.lang.IllegalArgumentException: bufferInfo must specify a valid buffer offset, size and presentation time

        at android.media.MediaMuxer.writeSampleData(MediaMuxer.java:682)

解决方案: 先调mediaMuxer.writeSampleData 后再mediaExtractor.advance();

合成时报如下错误,这个mediaMuxer.start之前没有添加轨道导致(流程不熟导致)**

java.lang.IllegalStateException: Failed to start the muxer
        at android.media.MediaMuxer.nativeStart(Native Method)
        at android.media.MediaMuxer.start(MediaMuxer.java:452)
start之前只是构造了mediaMuxer但没有mediaMuxer.addTrack(trackFormat);

在把音轨和视频轨道合成新视频时,复用了MediaTractor导致异常**

java.io.IOException: Failed to instantiate extractor.
com.av.mediajourney W/System.err:     at android.media.MediaExtractor.nativeSetDataSource(Native Method)
com.av.mediajourney W/System.err:     at android.media.MediaExtractor.setDataSource(MediaExtractor.java:203)
解决:在把视频轨道的源文件路径通过setDataSource设置到mediaextractor后,再把音轨的源文件setdataSource就报了这个错误
正确的做法是针对每一个源设置一个MediaExtractor,不同共用

把纯音轨和纯视频轨道合成新视频后,播放视频没有声音 时间是对的,但是没有声音**

猜测 会不会是因为轨道0是视频,轨道1是音频的原因? 用ffmpeg对比查看了下原视频和合成的视频这点有些差异,尝试调下顺序看下。

—>调整后没有效果。。。继续通过两证ffmpeg -i输出信息定位,发现metaChange不同

—>折腾了半天也没结果,最后通过ffplay来播放合成的视频,一切正常,只能说播放器的原因吧,也可能在合成时设置不全导致部分播放器无法播放,暂时不得结果

还出现一种情况是合成后的时长变成了音频加视频的时长总和了**

原因是bufferInfo.presentationTimeUs的值不对导致。

异常实现如下:

ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);

//先计算出视频帧间隔时间
long smapleTime = 0;
videoExtractor.readSampleData(byteBuffer, 0);
if (videoExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
    videoExtractor.advance();
}

videoExtractor.readSampleData(byteBuffer, 0);
long secondTime = videoExtractor.getSampleTime();
videoExtractor.advance();
long thirdtime = videoExtractor.getSampleTime();
smapleTime = Math.abs(thirdtime - secondTime);
Log.i(TAG, "muxerStart: smapleTime=" + smapleTime);
videoExtractor.unselectTrack(videoTrackIndex);
videoExtractor.selectTrack(videoTrackIndex);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int sampleSize = 0;

while ((sampleSize = videoExtractor.readSampleData(byteBuffer, 0)) > 0) {
    bufferInfo.flags = videoExtractor.getSampleFlags();
    bufferInfo.offset = 0;
    bufferInfo.size = sampleSize;
    bufferInfo.presentationTimeUs += smapleTime;
    //bufferInfo.presentationTimeUs = videoExtractor.getSampleTime();
    mediaMuxer.writeSampleData(videoTrackIndex, byteBuffer, bufferInfo);
    videoExtractor.advance();
}

int audioSampleSize = 0;
ByteBuffer audioByteBuffer = ByteBuffer.allocate(500 * 1024);
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();

while ((audioSampleSize = audioExtractor.readSampleData(byteBuffer, 0)) > 0) {
    audioBufferInfo.flags = audioExtractor.getSampleFlags();
    audioBufferInfo.offset = 0;
    audioBufferInfo.size = audioSampleSize;
    audioBufferInfo.presentationTimeUs += smapleTime;
    // audioBufferInfo.presentationTimeUs = videoExtractor.getSampleTime();
    mediaMuxer.writeSampleData(audioTrackIndex, audioByteBuffer, audioBufferInfo);
    audioExtractor.advance();
}

这些遇到的问题一部分是对mediaExtractor和mediaMuxer的流程不熟悉导致。而有些需要借助ffpmpeg和ffplay进行协助分析排查。文章来源地址https://www.toymoban.com/news/detail-808711.html

到了这里,关于MediaExtractor MediaMuxer 实现视频的解封装与合成的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ffmpeg解决bilibili下载的音视频分离问题,将音视频一键合成

    1:到FFmpeg下载安装包,我安装的是windows 下载打包文件 2:解压文件到本地 以下为解压后的文件视图 3:点击进去第一个bin文件,复制该目录,安装到本地环境变量中 然后点击确定进行保存设置 4:ctrl+r打开运行,输入cmd打开命令窗口,输入ffmpeg -version查看版本 安装成功 将所

    2024年02月11日
    浏览(45)
  • 干货 | 移动端使用OpenGL转场特效的音视频合成应用

    作者简介 jzg,携程资深前端开发工程师,专注Android开发; zx,携程高级前端开发工程师,专注iOS开发; zcc,携程资深前端开发工程师,专注iOS开发。 前言 近年来短视频的火爆,让内容创作类的APP获得了巨大的流量。用户通过这类工具编辑自己的短视频,添加各式各样的炫

    2024年02月04日
    浏览(50)
  • 【音视频原理】音视频 “ 采样 - 编码 - 封装 过程 “ 和 “ 解封装 - 解码 - 播放 过程 “ 分析 ( 视频采集处理流程 | 音频采集处理流程 | 音视频文件解封装播放流程 )

    本篇文件主要分析 音视频文件 是怎么产生的 , 以及 音视频文件是如何播放的 ; 视频文件从录像到生成文件的全过程 : 采集图像帧 : 摄像头 硬件 负责 采集画面 , 采集的 初始画面 称为 \\\" 图像帧 \\\" , 一秒钟 采集 的 图像帧 数量 称为 \\\" 帧率 \\\" , 如 : 60 帧 就是 一秒钟采集 60 个画

    2024年02月11日
    浏览(73)
  • 音视频——封装格式原理

    视频解码基础 一、封裝格式 ​ 我们播放的视频文件一般都是用一种 封装格式 封装起来的,封装格式的作用是什么呢?一般视频文件里不光有视频,还有音频,封装格式的作用就是把视频和音频打包起来。 所以我们先要 解封装格式 ,看有哪些视频流和哪些音频流,此时的

    2024年02月15日
    浏览(48)
  • 音视频 ffmpeg命令转封装

    保持编码格式: 改变编码格式: 修改帧率: 修改视频码率: 修改视频码率: 修改音频码率: 修改音视频码率: 修改视频分辨率: 修改音频采样率: 推荐一个零声学院项目课,个人觉得老师讲得不错,分享给大家: 零声白金学习卡(含基础架构/高性能存储/golang云原生/音

    2024年02月10日
    浏览(53)
  • FFmpeg入门详解之19:音视频封装原理简介

    什么是数据封装和解封装? 数据封装(baiData Encapsulation) ,笼统地讲,就是把业务数据映射到du某个封装协议zhi的净dao荷中,然后填充对应协议的包头,形成封装协议的数据包,并完成速率适配。 解封装 ,就是封装的逆过程,拆解协议包,处理包头中的信息,取出净荷中的业

    2023年04月09日
    浏览(46)
  • 【音视频 | opus】opus编码的Ogg封装文件详解

    😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭 🤣本文内容🤣:🍭介绍 opus 编码的 Ogg 封装文件🍭 😎金句分享😎:🍭🍭 本文未经允许,不得转发!!! opus和Ogg相关系列文章: 1、RFC3533 :Ogg封装格

    2024年02月03日
    浏览(62)
  • 【音视频开发】FFmpeg转换与封装 I - MP4格式

    1 FFmpeg转换与封装 1.1 MP4格式转换 1.1.1 MP4格式标准         FFmpeg支持的媒体封装格式具有多样性与全面性,与此, 我们还可以使用FFmpeg来对媒体格式进行转换与封装 。 在互联网常见的格式中,跨平台最好的应该是 MP4 文件,因为 MP4 文件既可以在PC 平台的Flashplayer中播放,

    2024年02月08日
    浏览(78)
  • 【音视频 | Ogg】Ogg封装格式详解——包含Ogg封装过程、数据包(packet)、页(page)、段(segment)等

    😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭 🤣本文内容🤣:🍭介绍Ogg文件格式🍭 😎金句分享😎:🍭子曰:见贤思齐焉,见不贤而内自省也。——《论语·里仁篇》。意思是,看见德才兼备的人就

    2024年02月06日
    浏览(48)
  • 【FFmpeg】ffmpeg 命令行参数 ⑧ ( 使用 ffmpeg 转换封装格式 | 音视频编解码器参数设置 | 视频 帧率 / 码率 / 分辨率 设置 | 音频 码率 / 采样率 设置 )

    音视频 文件 从 采样 - 处理 - 得到原始数据帧队列 - 音视频编码 - 音视频包队列 - 格式封装 的过程如下 : 封装格式 参考 【音视频原理】音视频 “ 采样 - 编码 - 封装 过程 “ 和 “ 解封装 - 解码 - 播放 过程 “ 分析 ( 视频采集处理流程 | 音频采集处理流程 | 音视频文件解封装

    2024年04月17日
    浏览(79)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包