feat: new AVC box implementation
This commit is contained in:
parent
1c406419a5
commit
309ecb9a79
3 changed files with 131 additions and 7 deletions
51
AVCVideoStream.go
Normal file
51
AVCVideoStream.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2024 Martin Riedl
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package gomp4
|
||||||
|
|
||||||
|
type AVCSampleEntry struct {
|
||||||
|
*VisualSampleEntry
|
||||||
|
// TODO: some more
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoxTypeAVCSampleEntry1 AVC Sample Entry Box
|
||||||
|
const BoxTypeAVCSampleEntry1 = "avc1"
|
||||||
|
|
||||||
|
// BoxTypeAVCSampleEntry3 AVC Sample Entry Box
|
||||||
|
const BoxTypeAVCSampleEntry3 = "avc3"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
BoxDefinitions = append(BoxDefinitions, BoxDefinition{
|
||||||
|
Type: BoxTypeAVCSampleEntry1,
|
||||||
|
ParentTypes: []string{BoxTypeSampleDescription},
|
||||||
|
Parser: ParseAVCSampleEntry,
|
||||||
|
}, BoxDefinition{
|
||||||
|
Type: BoxTypeAVCSampleEntry3,
|
||||||
|
ParentTypes: []string{BoxTypeSampleDescription},
|
||||||
|
Parser: ParseAVCSampleEntry,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAVCSampleEntry creates a new AVC sample entry box struct based on bytes
|
||||||
|
func ParseAVCSampleEntry(parser *Parser, filePosition uint64, headerSize uint32, content []byte) (any, error) {
|
||||||
|
sampleEntry, position := ParseVisualSampleEntry(filePosition, headerSize, content)
|
||||||
|
box := &AVCSampleEntry{
|
||||||
|
VisualSampleEntry: sampleEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: parse other fields
|
||||||
|
_ = position
|
||||||
|
|
||||||
|
return box, nil
|
||||||
|
}
|
10
README.md
10
README.md
|
@ -3,7 +3,7 @@
|
||||||
[](https://gitlab.com/martinr92/gomp4/commits/main)
|
[](https://gitlab.com/martinr92/gomp4/commits/main)
|
||||||
[](https://gitlab.com/martinr92/gomp4/commits/main)
|
[](https://gitlab.com/martinr92/gomp4/commits/main)
|
||||||
|
|
||||||
mp4 implementation in golang based on spec ISO ICE 14496-12:2015
|
mp4 implementation in golang based on spec ISO/IEC 14496-12:2015
|
||||||
|
|
||||||
## Parser
|
## Parser
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ if err := parser.Parse(); err != nil {
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
Implementation progress of ISO ICE 14496-12:2015:
|
Implementation progress of ISO/IEC 14496-12:2015:
|
||||||
|
|
||||||
| Chapter | Box Types | Parser |
|
| Chapter | Box Types | Parser |
|
||||||
|----------------------------------------------------|----------------|-------:|
|
|----------------------------------------------------|----------------|-------:|
|
||||||
|
@ -118,6 +118,12 @@ Implementation progress of ISO ICE 14496-12:2015:
|
||||||
| 12.2.7 Audio stream loudness | ludt | - |
|
| 12.2.7 Audio stream loudness | ludt | - |
|
||||||
| 12.4.2 Hint Media Header Box | hmhd | - |
|
| 12.4.2 Hint Media Header Box | hmhd | - |
|
||||||
|
|
||||||
|
Implementation progress of ISO/IEC 14496-15:2017:
|
||||||
|
|
||||||
|
| Chapter | Box Types | Parser |
|
||||||
|
|----------------------------------------------------|------------------------------------------|-------:|
|
||||||
|
| 5.4.2 AVC video stream definition | avc1, avc2, avc3, avc4, avcC, m4ds, btrt | 10% |
|
||||||
|
|
||||||
## Helper Tools
|
## Helper Tools
|
||||||
|
|
||||||
- [Online MP4 file parser](https://www.onlinemp4parser.com/)
|
- [Online MP4 file parser](https://www.onlinemp4parser.com/)
|
||||||
|
|
|
@ -17,7 +17,6 @@ package gomp4
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SampleDescriptionBox sample description box struct
|
// SampleDescriptionBox sample description box struct
|
||||||
|
@ -112,17 +111,85 @@ type SampleEntry struct {
|
||||||
DataReferenceIndex uint16
|
DataReferenceIndex uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseSampleEntry(filePosition uint64, headerSize uint32, content []byte) *SampleEntry {
|
func ParseSampleEntry(filePosition uint64, headerSize uint32, content []byte) (*SampleEntry, int) {
|
||||||
box := &SampleEntry{
|
box := &SampleEntry{
|
||||||
Box: &Box{filePosition, headerSize},
|
Box: &Box{filePosition, headerSize},
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip reserved
|
// skip reserved
|
||||||
position := 2 * 8
|
position := 6
|
||||||
|
|
||||||
// parse reference index
|
// parse reference index
|
||||||
box.DataReferenceIndex = binary.BigEndian.Uint16(content[position : position+2])
|
box.DataReferenceIndex = binary.BigEndian.Uint16(content[position : position+2])
|
||||||
log.Println("data reference index", box.DataReferenceIndex) // TODO: remove me
|
position += 2
|
||||||
|
|
||||||
return box
|
return box, position
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisualSampleEntry visual sample entry struct
|
||||||
|
//
|
||||||
|
// 12.1.3 Sample entry
|
||||||
|
//
|
||||||
|
// Video tracks use VisualSampleEntry.
|
||||||
|
//
|
||||||
|
// In video tracks, the frame_count field must be 1 unless the specification for the media format explicitly
|
||||||
|
// documents this template field and permits larger values. That specification must document both how
|
||||||
|
// the individual frames of video are found (their size information) and their timing established. That
|
||||||
|
// timing might be as simple as dividing the sample duration by the frame count to establish the frame
|
||||||
|
// duration.
|
||||||
|
type VisualSampleEntry struct {
|
||||||
|
*SampleEntry
|
||||||
|
Width uint16
|
||||||
|
Height uint16
|
||||||
|
// give the resolution of the image in pixels‐per‐inch, as a fixed 16.16 number
|
||||||
|
HorizResolution Fixed1616
|
||||||
|
// give the resolution of the image in pixels‐per‐inch, as a fixed 16.16 number
|
||||||
|
VertResolution Fixed1616
|
||||||
|
// indicates how many frames of compressed video are stored in each sample. The
|
||||||
|
// default is 1, for one frame per sample; it may be more than 1 for multiple frames per sample
|
||||||
|
FrameCount uint16
|
||||||
|
// is a name, for informative purposes. It is formatted in a fixed 32‐byte field, with
|
||||||
|
//the first byte set to the number of bytes to be displayed, followed by that number of bytes of
|
||||||
|
//displayable data, and then padding to complete 32 bytes total (including the size byte). The field
|
||||||
|
//may be set to 0.
|
||||||
|
CompressorName string
|
||||||
|
Depth uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseVisualSampleEntry(filePosition uint64, headerSize uint32, content []byte) (*VisualSampleEntry, int) {
|
||||||
|
sampleEntry, position := ParseSampleEntry(filePosition, headerSize, content)
|
||||||
|
box := &VisualSampleEntry{
|
||||||
|
SampleEntry: sampleEntry,
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip pre-defined and reserved bytes
|
||||||
|
position += 2 + 2 + 4*3
|
||||||
|
|
||||||
|
// parse resolution
|
||||||
|
box.Width = binary.BigEndian.Uint16(content[position : position+2])
|
||||||
|
box.Height = binary.BigEndian.Uint16(content[position+2 : position+4])
|
||||||
|
box.HorizResolution = NewFixed1616ByBytes(content[position+4 : position+8])
|
||||||
|
box.VertResolution = NewFixed1616ByBytes(content[position+8 : position+12])
|
||||||
|
position += 12
|
||||||
|
|
||||||
|
// skip reserved bytes
|
||||||
|
position += 4
|
||||||
|
|
||||||
|
// parse frame count
|
||||||
|
box.FrameCount = binary.BigEndian.Uint16(content[position : position+2])
|
||||||
|
position += 2
|
||||||
|
|
||||||
|
// parse compressor name length (32bytes total)
|
||||||
|
compressorNameLength := int(content[position])
|
||||||
|
box.CompressorName = string(content[position+1 : position+1+compressorNameLength])
|
||||||
|
position += 32
|
||||||
|
|
||||||
|
// parse depth
|
||||||
|
box.Depth = binary.BigEndian.Uint16(content[position : position+2])
|
||||||
|
position += 2
|
||||||
|
|
||||||
|
// skip pre-defined bytes
|
||||||
|
position += 2
|
||||||
|
|
||||||
|
return box, position
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue