Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 为.ts段创建MP4容器_Android_Video_Http Live Streaming - Fatal编程技术网

Android 为.ts段创建MP4容器

Android 为.ts段创建MP4容器,android,video,http-live-streaming,Android,Video,Http Live Streaming,我想在我的Android应用程序上播放静态HLS内容,而不是实时视频内容。我目前所做的是从.m3u8文件下载所有段,并将其合并到一个文件中。当我播放此文件时,我可以看到正在播放的视频,但它不可查看。因此,.ts文件在Android上不可查找 我不能冒险在手机上运行ffmpeg将文件转换为MP4格式。我研究了MP4格式及其原子结构。我想知道的是,是否有一种简单的方法来创建MP4容器原子层次结构,该层次结构将简单地引用.ts段-从其数据原子mdat中的子段创建的合并段 如果您能提供任何帮助/建议,我

我想在我的Android应用程序上播放静态HLS内容,而不是实时视频内容。我目前所做的是从.m3u8文件下载所有段,并将其合并到一个文件中。当我播放此文件时,我可以看到正在播放的视频,但它不可查看。因此,.ts文件在Android上不可查找

我不能冒险在手机上运行ffmpeg将文件转换为MP4格式。我研究了MP4格式及其原子结构。我想知道的是,是否有一种简单的方法来创建MP4容器原子层次结构,该层次结构将简单地引用.ts段-从其数据原子mdat中的子段创建的合并段


如果您能提供任何帮助/建议,我将不胜感激。

没有副本是不可能的。TS is使用带有报头的188字节数据包。这些报头在帧的中间创建中断。在mp4中,帧必须是连续的

Android提供了支持库,如MediaCodec和MediaExtractor,可访问低级媒体编码/解码。它使用硬件加速,速度快,效率高

我认为,除非您同意使用ffmpeg(当然,如果资源密集型操作),否则在Android上就应该这样做

1使用MediaExtractor从文件中提取数据

2将提取的数据传递给MediaCodec

3如果是视频,使用MediaCodec将输出渲染到曲面,如果是音频,则使用AudioTrack

4这是最困难的步骤:同步音频/视频。我还没有实现这个。但这需要跟踪音频和视频之间的时间同步。音频将正常播放,在视频播放的情况下,您可能必须删除一些帧,以使其与音频播放同步

下面是解码音频/视频并分别使用AudioTrack和Surface播放它们的代码。 在视频解码的情况下,有一个睡眠来减缓帧渲染

public void decodeVideo(Surface surface) throws IOException {
    MediaExtractor extractor = new MediaExtractor();
    MediaCodec codec;
    ByteBuffer[] codecInputBuffers;
    ByteBuffer[] codecOutputBuffers;

    extractor.setDataSource(file);

    Log.d(TAG, "No of tracks = " + extractor.getTrackCount());
    MediaFormat format = extractor.getTrackFormat(0);

    String mime = format.getString(MediaFormat.KEY_MIME);
    Log.d(TAG, "mime = " + mime);
    Log.d(TAG, "format = " + format);

    codec = MediaCodec.createDecoderByType(mime);
    codec.configure(format, surface, null, 0);
    codec.start();
    codecInputBuffers = codec.getInputBuffers();
    codecOutputBuffers = codec.getOutputBuffers();

    extractor.selectTrack(0);

    final long timeout_in_Us = 5000;
    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    boolean sawInputEOS = false;
    boolean sawOutputEOS = false;
    int noOutputCounter = 0;

    long startMs = System.currentTimeMillis();

    while(!sawOutputEOS && noOutputCounter < 50) {
        noOutputCounter++;
        if(!sawInputEOS) {
            int inputBufIndex = codec.dequeueInputBuffer(timeout_in_Us);

            if(inputBufIndex >= 0) {
                ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];

                int sampleSize = extractor.readSampleData(dstBuf, 0);

                long presentationTimeUs = 0;

                if(sampleSize < 0) {
                    Log.d(TAG, "saw input EOS.");
                    sawInputEOS = true;
                    sampleSize = 0;
                } else {
                    presentationTimeUs = extractor.getSampleTime();
                }

                codec.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);

                if(!sawInputEOS) {
                    extractor.advance();
                }
            }

        }

        int res = codec.dequeueOutputBuffer(info, timeout_in_Us);

        if(res >= 0) {

            if(info.size > 0) {
                noOutputCounter = 0;
            }

            int outputBufIndex = res;

            while(info.presentationTimeUs/1000 > System.currentTimeMillis() - startMs) {
                try {
                    Thread.sleep(5);
                } catch (Exception e) {
                    break;
                }
            }

            codec.releaseOutputBuffer(outputBufIndex, true);

            if((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                Log.d(TAG, "saw output EOS.");
                sawOutputEOS = true;
            }

        } else if(res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            codecOutputBuffers = codec.getOutputBuffers();
            Log.d(TAG, "output buffers have changed.");

        } else if(res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            MediaFormat format1 = codec.getOutputFormat();
            Log.d(TAG, "output format has changed to " + format1);
        } else if(res == MediaCodec.INFO_TRY_AGAIN_LATER) {
            Log.d(TAG, "Codec try again returned" + res);
        }
    }

    codec.stop();
    codec.release();
}

private int audioSessionId = -1;
private AudioTrack createAudioTrack(MediaFormat format) {
    int channelConfiguration = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT) == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;
    int bufferSize = AudioTrack.getMinBufferSize(format.getInteger(MediaFormat.KEY_SAMPLE_RATE), channelConfiguration, AudioFormat.ENCODING_PCM_16BIT) * 8;

    AudioTrack audioTrack;
    if(audioSessionId == -1) {
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, format.getInteger(MediaFormat.KEY_SAMPLE_RATE), channelConfiguration,
                AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
    } else {
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, format.getInteger(MediaFormat.KEY_SAMPLE_RATE), channelConfiguration,
                AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM, audioSessionId);
    }
    audioTrack.play();
    audioSessionId = audioTrack.getAudioSessionId();
    return audioTrack;
}

public void decodeAudio() throws IOException {
    MediaExtractor extractor = new MediaExtractor();
    MediaCodec codec;
    ByteBuffer[] codecInputBuffers;
    ByteBuffer[] codecOutputBuffers;

    extractor.setDataSource(file);

    Log.d(TAG, "No of tracks = " + extractor.getTrackCount());
    MediaFormat format = extractor.getTrackFormat(1);

    String mime = format.getString(MediaFormat.KEY_MIME);
    Log.d(TAG, "mime = " + mime);
    Log.d(TAG, "format = " + format);

    codec = MediaCodec.createDecoderByType(mime);
    codec.configure(format, null, null, 0);
    codec.start();
    codecInputBuffers = codec.getInputBuffers();
    codecOutputBuffers = codec.getOutputBuffers();

    extractor.selectTrack(1);

    AudioTrack audioTrack = createAudioTrack(format);

    final long timeout_in_Us = 5000;
    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
    boolean sawInputEOS = false;
    boolean sawOutputEOS = false;
    int noOutputCounter = 0;

    while(!sawOutputEOS && noOutputCounter < 50) {
        noOutputCounter++;
        if(!sawInputEOS) {
            int inputBufIndex = codec.dequeueInputBuffer(timeout_in_Us);

            if(inputBufIndex >= 0) {
                ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];

                int sampleSize = extractor.readSampleData(dstBuf, 0);

                long presentationTimeUs = 0;

                if(sampleSize < 0) {
                    Log.d(TAG, "saw input EOS.");
                    sawInputEOS = true;
                    sampleSize = 0;
                } else {
                    presentationTimeUs = extractor.getSampleTime();
                }

                codec.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);

                if(!sawInputEOS) {
                    extractor.advance();
                }
            }

        }

        int res = codec.dequeueOutputBuffer(info, timeout_in_Us);
        if(res >= 0) {

            if(info.size > 0) {
                noOutputCounter = 0;
            }

            int outputBufIndex = res;
            //Possibly store the decoded buffer
            ByteBuffer buf = codecOutputBuffers[outputBufIndex];
            final byte[] chunk = new byte[info.size];
            buf.get(chunk);
            buf.clear();

            if(chunk.length > 0) {
                audioTrack.write(chunk, 0 ,chunk.length);
            }

            codec.releaseOutputBuffer(outputBufIndex, false);

            if((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                Log.d(TAG, "saw output EOS.");
                sawOutputEOS = true;
            }

        } else if(res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            codecOutputBuffers = codec.getOutputBuffers();
            Log.d(TAG, "output buffers have changed.");

        } else if(res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            MediaFormat format1 = codec.getOutputFormat();
            Log.d(TAG, "output format has changed to " + format1);
            audioTrack.stop();
            audioTrack = createAudioTrack(codec.getOutputFormat());
        } else if(res == MediaCodec.INFO_TRY_AGAIN_LATER) {
            Log.d(TAG, "Codec try again returned" + res);
        }
    }

    codec.stop();
    codec.release();
}

谢谢你的回复。我想这就是为什么我不能演奏它的原因。你知道有没有办法解码TS文件,获取帧,然后用它创建MP4。我知道ffmpeg就是这么做的,但它的源代码非常复杂。此外,我正在考虑一个基于JAVA的实现。在你看来,有没有更好的方法来处理这个问题?你不想解码。你只想转化。我不知道Java中有哪些LIB可用,但您可以自己编写。谢谢。这就是我打算做的。你知道对trasmux有用的参考资料吗。或者如果你能建议如何继续。所有这些视频格式对我来说都是新的。我的意思是,我一直在使用这些格式,但只是播放视频,从来没有在这样的粒度级别。我在《传输流》中的维基百科文章非常好。您还需要了解基本流格式。Mp3帧头、ADTS头、序列头/额外数据。我有一个关于AVCVC的帖子,特别是在这里,谢谢你的参考。这就是我计划要做的。如果我错了,请纠正我。首先,我需要从TS文件音频和视频中获取基本流。其次,了解如何将这些流放入MP4容器中。这种方法有效吗?