android 边录制视频边写软字幕

        目前,对于边录制视频,边要显示时间戳的需求,都是通过用对应字符的bitmap图片的yuv数据,来替换每一帧yuv数据的像素点来实现的。这样做的坏处显而易见,这个时间戳数据,是硬生生的印在每一帧数据上的,无法剥离。不能在播放的时候,由用户来控制显不显示。不能动态的配置时间戳字体颜色、大小等样式。

        要想解决这个问题,可以用给视频配置对应的字幕来实现。当然,如果一边录一边生成一个*.srt外挂字幕文件,也可以达到目的。不过外挂字幕,终归是要多生成一个文件,让人感觉到不爽。 

        最好的解决办法,就是边录边写内嵌软字幕。不过现在给视频加字幕,都是在视频录制完后,通过ffmpeg或mp4box来将字幕文件里的字幕打入到mp4里去的。边录边写字幕,这个还没有人做过,网上也没有任何资料。为了解决这个问题,本人参考了ffmpeg、mp4box、vlc源代码,以及用到了mp4info、mediainfo等软件来分析。

        要想给视频加上字幕,就必须先了解mp4格式。在mp4里,每一类型的数据,集中在一块,称为一个轨道track。比如最常见的视频轨和音频轨,当然还有我们关心的字幕轨。一个正常的mp4里面的结构如下图所示:

        

在这个图里面,一共有三个track,前两个为音视频track,最后一个,可以从展开的box里面看到,它为tx3g,也即字幕轨。

用mp4box -info *.mp4来查看,也可以看到mp4里面的大致数据结构:

* Movie Info *
        Timescale 1000 - 3 tracks
        Computed Duration 00:00:17.262 - Indicated Duration 00:00:17.252
        Fragmented File: no
        File suitable for progressive download (moov before mdat)
        File Brand mp42 - version 0
                Compatible brands: isom mp42
        Created: GMT Wed May 15 09:55:46 2019
        Modified: GMT Wed May 15 09:55:46 2019

File has no MPEG4 IOD/OD

Track # 1 Info - TrackID 1 - TimeScale 90000
Media Duration 00:00:17.207 - Indicated Duration 00:00:17.176
Media Info: Language "Undetermined (und)" - Type "vide:avc1" - 513 samples
Visual Track layout: x=0 y=0 width=1920 height=1088
MPEG-4 Config: Visual Stream - ObjectTypeIndication 0x21
AVC/H264 Video - Visual Size 1920 x 1088
        AVC Info: 1 SPS - 1 PPS - Profile Baseline @ Level 4.1
        NAL Unit length bits: 32
        Chroma format YUV 4:2:0 - Luma bit depth 8 - chroma bit depth 8
        SPS#1 hash: C7866EA651F1014BB3FE916BCC6CF22764001970
        PPS#1 hash: 7F4E64F60527715F38B21D7814E9A094754DBFF2
Self-synchronized
        RFC6381 Codec Parameters: avc1.42C029
        Average GOP length: 32 samples

Track # 2 Info - TrackID 2 - TimeScale 44100
Media Duration 00:00:17.262 - Indicated Duration 00:00:17.252
Media Info: Language "Undetermined (und)" - Type "soun:mp4a" - 743 samples
MPEG-4 Config: Audio Stream - ObjectTypeIndication 0x40
MPEG-4 Audio AAC LC - 1 Channel(s) - SampleRate 44100
Synchronized on stream 1
        RFC6381 Codec Parameters: mp4a.40.2
        All samples are sync

Track # 3 Info - TrackID 3 - TimeScale 1000000
Media Duration 00:00:17.000 - Indicated Duration 00:00:17.000
Media Info: Language "Undetermined (und)" - Type "sbtl:tx3g" - 17 samples
Unknown Text Stream
 Size 0 x 0 - Translation X=0 Y=0 - Layer 0
        RFC6381 Codec Parameters: tx3g
Alternate Group ID 3
        All samples are sync

       这里可以看第trackId 为3的这个track,是个text stream流,也即这个mp4文件里,包含了一个字幕流。

       如果我们要让我们的录制的mp4里包含字幕流,那么必须我们在录制mp4的时候,必须往里面写入一个字幕轨。   本人用到的android工程是android6.0,不过在mp4模块,无论是6.0还是9.0,无论是展迅还是高通平台,代码逻辑都是一样的。

        android里,在录制视频的时候,会给视频添加两个源source,一个是audio source,一个是camera source, 这个可以从frameworks\av\media\libmediaplayerservice\StagefrightRecorder.cpp文件里的setupAudioEncoder和setupMPEG4orWEBMRecording函数里看到,添加音视频流是writer->addSource。

        其中,音频流对应的文件是frameworks\av\media\libstagefright\AudioSource.cpp,视频流对应的文件是frameworks\av\media\libstagefright\CameraSource.cpp。

       当配置好音视频源后,在对应的frameworks\av\media\libstagefright\MPEG4Writer.cpp文件里的MPEG4Writer::Track::threadEntry()里,通过while (!mDone && (err = mSource->read(&buffer)) == OK),会源源不断的从mic和摄像头里,获取音视频数据。然后将获取到的数据,通过mChunkSamples.push_back(copy),将它们放到一个ChunkSamples列表里。当获取到的chunk达到一定数目后,再通过writeChunkToFile,将它写入到mp4里去。

        从这个流程来看,我们要加字幕流的话,显然也需要加上一个字幕源加入到这个录制逻辑里去。我们的字幕源,我暂且叫它为SubtitleSource,它不同于音视频源,它的数据,不是从mic或摄像头这些硬件里采集来的。它只需要,每隔一秒钟,就采集一次当前的时间,然后将它传给mSource->read(&buffer),就可以了。当然这个源里的数据,要严格按照mp4的字幕流格式来排列,否则显示不了。

        同时,我们不仅仅要添加这个源,还需要针对mp4的字幕轨,新建它特有的box。比如Sthd、Nmhd、Edts、Elst、stsd等box,还需要配置字幕的字体样式。

        还有getFourCCForMime这个函数也要作对应的修改,原始的这个函数,只对audio和video的mime type做了处理,我们还需要加上对text类似的处理。 status_t MPEG4Writer::addSource(const sp<MediaSource> &source) 这个函数里,要将if (mTracks.size() >= 2)改成if (mTracks.size() >= 3)。

        基本的逻辑大致就是这样,具体的修改后的代码,我会上传到我的博客资源里,有兴趣的可以自己下载下来。

猜你喜欢

转载自blog.csdn.net/xuhui_7810/article/details/90261985
今日推荐